From 57f9b70665e3f8d928a9fe15008c2d46b0dffdc5 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Tue, 12 May 2026 22:13:26 +0530 Subject: [PATCH 01/79] refactor: core sandbox infra to use openenv.core.harness.sandbox --- envs/opencode_env/__init__.py | 7 ++- envs/opencode_env/harness.py | 7 ++- envs/opencode_env/sandbox/__init__.py | 51 +++---------------- envs/opencode_env/sandbox/build_template.py | 10 ++-- src/openenv/core/harness/sandbox/__init__.py | 31 +++++++++++ .../openenv/core/harness}/sandbox/base.py | 10 ++-- .../core/harness/sandbox/e2b_backend.py | 11 +++- .../core/harness}/sandbox/interception.py | 8 +-- tests/envs/test_opencode_env.py | 2 +- 9 files changed, 74 insertions(+), 63 deletions(-) create mode 100644 src/openenv/core/harness/sandbox/__init__.py rename {envs/opencode_env => src/openenv/core/harness}/sandbox/base.py (87%) rename envs/opencode_env/sandbox/e2b.py => src/openenv/core/harness/sandbox/e2b_backend.py (94%) rename {envs/opencode_env => src/openenv/core/harness}/sandbox/interception.py (98%) diff --git a/envs/opencode_env/__init__.py b/envs/opencode_env/__init__.py index 223be6f7b..17cd145b3 100644 --- a/envs/opencode_env/__init__.py +++ b/envs/opencode_env/__init__.py @@ -30,7 +30,12 @@ RolloutResult, RolloutTurn, ) -from .sandbox import E2BSandboxBackend, SandboxBackend, SandboxHandle +from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle + +try: + from openenv.core.harness.sandbox import E2BSandboxBackend +except ImportError: # e2b not installed + E2BSandboxBackend = None # type: ignore[assignment,misc] from .task import OpenCodeTask __all__ = [ diff --git a/envs/opencode_env/harness.py b/envs/opencode_env/harness.py index da4410dd4..dc0eb55be 100644 --- a/envs/opencode_env/harness.py +++ b/envs/opencode_env/harness.py @@ -52,7 +52,7 @@ opencode_config_path, system_prompt_path, ) -from .sandbox.base import BgJob, SandboxBackend, SandboxHandle +from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle from .task import OpenCodeTask @@ -64,7 +64,10 @@ # Where the proxy source lives on disk (in this repo). Uploaded into the # sandbox at /home/user/proxy/interception.py before each rollout, unless # the sandbox was created from a template that already has it baked in. -_PROXY_SOURCE_PATH = Path(__file__).parent / "sandbox" / "interception.py" +_PROXY_SOURCE_PATH = ( + Path(__file__).resolve().parents[2] + / "src" / "openenv" / "core" / "harness" / "sandbox" / "interception.py" +) Verifier = Callable[[SandboxHandle, OpenCodeTask], VerifyResult] diff --git a/envs/opencode_env/sandbox/__init__.py b/envs/opencode_env/sandbox/__init__.py index 321f81547..a3496a2b1 100644 --- a/envs/opencode_env/sandbox/__init__.py +++ b/envs/opencode_env/sandbox/__init__.py @@ -4,50 +4,13 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Sandbox backends for the OpenCode harness. +"""Sandbox backends — re-exported from ``openenv.core.harness.sandbox``. -The primitive ships with :class:`E2BSandboxBackend` as the default; any backend -that satisfies the :class:`SandboxBackend` / :class:`SandboxHandle` protocols -can be swapped in. - -The ``e2b`` import is wrapped in ``try/except`` so this package can be loaded -in environments where ``e2b`` isn't installed (CI smoke tests, lint runs). -Instantiating ``E2BSandboxBackend`` without ``e2b`` raises a clear error. +The canonical source for sandbox protocols and implementations now lives in +``src/openenv/core/harness/sandbox/``. This package re-exports everything +so that ``from opencode_env.sandbox import ...`` keeps working, but all new +code should import from ``openenv.core.harness.sandbox`` directly. """ -from .base import BgJob, ExecResult, SandboxBackend, SandboxHandle - -try: - from .e2b import E2BBgJob, E2BSandboxBackend, E2BSandboxHandle # noqa: F401 -except ImportError as _e2b_err: # pragma: no cover - - class _RequiresE2B: - """Stub raised when ``e2b`` is not installed. - - Lets the package import cleanly so unit tests, ``openenv validate``, - and the docs build can run without the heavy ``e2b`` dependency. - Actually constructing one of these classes raises a clear ImportError. - """ - - _e2b_import_error = _e2b_err - - def __init__(self, *_args, **_kwargs): - raise ImportError( - "e2b is not installed; install it via " - "`pip install 'openenv-opencode-env[dev]'` or " - "`pip install e2b` to use E2BSandboxBackend. " - f"Original import error: {self._e2b_import_error}" - ) - - E2BBgJob = E2BSandboxBackend = E2BSandboxHandle = _RequiresE2B # type: ignore[assignment] - - -__all__ = [ - "BgJob", - "ExecResult", - "SandboxBackend", - "SandboxHandle", - "E2BBgJob", - "E2BSandboxBackend", - "E2BSandboxHandle", -] +from openenv.core.harness.sandbox import * # noqa: F401,F403 +from openenv.core.harness.sandbox import __all__ # noqa: F401 diff --git a/envs/opencode_env/sandbox/build_template.py b/envs/opencode_env/sandbox/build_template.py index 01c32d537..084a95e64 100644 --- a/envs/opencode_env/sandbox/build_template.py +++ b/envs/opencode_env/sandbox/build_template.py @@ -44,8 +44,10 @@ from e2b import Template, default_build_logger -_ENV_DIR = Path(__file__).resolve().parent -_PROXY_SOURCE = _ENV_DIR / "interception.py" +_REPO_ROOT = Path(__file__).resolve().parents[3] +_PROXY_SOURCE = ( + _REPO_ROOT / "src" / "openenv" / "core" / "harness" / "sandbox" / "interception.py" +) def _load_env(path: Path) -> None: @@ -91,7 +93,7 @@ def build_template(name: str, *, skip_cache: bool = False) -> str: .make_dir("/home/user/task") .make_dir("/home/user/workdir") .make_dir("/home/user/proxy") - .copy("interception.py", "/home/user/proxy/interception.py") + .copy(str(_PROXY_SOURCE), "/home/user/proxy/interception.py") .set_workdir("/home/user/workdir") ) if skip_cache: @@ -121,7 +123,7 @@ def main(argv: list[str] | None = None) -> int: ) args = p.parse_args(argv) - _load_env(_ENV_DIR / ".env") + _load_env(_REPO_ROOT / "envs" / "opencode_env" / "sandbox" / ".env") if not os.environ.get("E2B_API_KEY"): print("ERROR: E2B_API_KEY required.", file=sys.stderr) return 2 diff --git a/src/openenv/core/harness/sandbox/__init__.py b/src/openenv/core/harness/sandbox/__init__.py new file mode 100644 index 000000000..d0324a7d7 --- /dev/null +++ b/src/openenv/core/harness/sandbox/__init__.py @@ -0,0 +1,31 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Sandbox backends for harness-driven rollouts. + +Provides the :class:`SandboxBackend` / :class:`SandboxHandle` protocols and +concrete implementations. Any harness adapter can use any backend — the +sandbox layer is orthogonal to the agent CLI choice. + +The ``e2b`` import is wrapped in ``try/except`` so this package loads cleanly +in environments where ``e2b`` isn't installed (CI smoke tests, lint runs). +""" + +from .base import BgJob, ExecResult, SandboxBackend, SandboxHandle + +__all__ = [ + "BgJob", + "ExecResult", + "SandboxBackend", + "SandboxHandle", +] + +try: + from .e2b_backend import E2BBgJob, E2BSandboxBackend, E2BSandboxHandle + + __all__.extend(["E2BBgJob", "E2BSandboxBackend", "E2BSandboxHandle"]) +except ImportError: + pass # e2b not installed — stubs live in envs/opencode_env/sandbox/__init__.py diff --git a/envs/opencode_env/sandbox/base.py b/src/openenv/core/harness/sandbox/base.py similarity index 87% rename from envs/opencode_env/sandbox/base.py rename to src/openenv/core/harness/sandbox/base.py index 76869149a..4b2620799 100644 --- a/envs/opencode_env/sandbox/base.py +++ b/src/openenv/core/harness/sandbox/base.py @@ -6,12 +6,12 @@ """Sandbox backend protocol. -A ``SandboxBackend`` produces ``SandboxHandle`` instances that the harness uses -to stage files, run the OpenCode install, launch the agent as a background -process, and later tear the sandbox down. +A ``SandboxBackend`` produces ``SandboxHandle`` instances that harnesses use +to stage files, install agent CLIs, launch the agent as a background process, +and later tear the sandbox down. -Backends can be implemented against any provider (E2B, Docker, Modal, Prime) -as long as they satisfy the Protocols defined here. +Backends can be implemented against any provider (E2B, CubeSandbox, Docker, +Modal) as long as they satisfy the Protocols defined here. """ from __future__ import annotations diff --git a/envs/opencode_env/sandbox/e2b.py b/src/openenv/core/harness/sandbox/e2b_backend.py similarity index 94% rename from envs/opencode_env/sandbox/e2b.py rename to src/openenv/core/harness/sandbox/e2b_backend.py index b567a9e65..f344346ba 100644 --- a/envs/opencode_env/sandbox/e2b.py +++ b/src/openenv/core/harness/sandbox/e2b_backend.py @@ -4,7 +4,14 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""E2B implementation of :class:`SandboxBackend`.""" +"""E2B implementation of :class:`SandboxBackend`. + +Works with both E2B cloud (api.e2b.dev) and self-hosted E2B-compatible +backends like CubeSandbox. For CubeSandbox, set:: + + E2B_API_URL=http://your-cubesandbox:3000 + E2B_API_KEY=dummy # any non-empty string +""" from __future__ import annotations @@ -15,7 +22,7 @@ from e2b import Sandbox from e2b.sandbox_sync.commands.command_handle import CommandHandle -from .base import BgJob, ExecResult, SandboxBackend, SandboxHandle +from openenv.core.harness.sandbox.base import BgJob, ExecResult, SandboxBackend, SandboxHandle class E2BBgJob: diff --git a/envs/opencode_env/sandbox/interception.py b/src/openenv/core/harness/sandbox/interception.py similarity index 98% rename from envs/opencode_env/sandbox/interception.py rename to src/openenv/core/harness/sandbox/interception.py index 131d41024..dc3dbe5be 100644 --- a/envs/opencode_env/sandbox/interception.py +++ b/src/openenv/core/harness/sandbox/interception.py @@ -6,15 +6,15 @@ """Transparent OpenAI-compatible forwarding proxy with logprob capture. -The proxy is a small FastAPI app that OpenCode talks to instead of the upstream -LLM endpoint. It: +The proxy is a small FastAPI app that agent CLIs (OpenCode, Claude Code, +Codex, Pi, etc.) talk to instead of the upstream LLM endpoint. It: 1. Forwards every ``POST /v1/chat/completions`` request to the real upstream URL, injecting ``logprobs=true`` and ``top_logprobs=N`` so the upstream returns per-token logprobs. 2. Captures each ``(request, response, logprobs)`` triple to a JSON-lines trace file. -3. Returns the upstream response to OpenCode verbatim (minus the ``logprobs`` +3. Returns the upstream response to the agent verbatim (minus the ``logprobs`` field, which we strip so the CLI never sees anything unexpected). The proxy is stateless beyond the trace file. One proxy instance runs per @@ -22,7 +22,7 @@ Run standalone:: - OPENCODE_UPSTREAM_API_KEY=... python -m opencode_env.interception \\ + UPSTREAM_API_KEY=... python -m openenv.core.harness.sandbox.interception \\ --upstream-url https://vllm.example/v1 \\ --trace /tmp/trace.jsonl \\ --port 7000 diff --git a/tests/envs/test_opencode_env.py b/tests/envs/test_opencode_env.py index 812ade194..6014c9199 100644 --- a/tests/envs/test_opencode_env.py +++ b/tests/envs/test_opencode_env.py @@ -309,7 +309,7 @@ def _exec_with_retry(self, *args, **kwargs): def test_interception_cli_reads_upstream_key_from_env( monkeypatch: pytest.MonkeyPatch, ) -> None: - from opencode_env.sandbox import interception + from openenv.core.harness.sandbox import interception captured = {} From 024e9042c3833fbd253f7c9981eb6909f29fb0f1 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Tue, 12 May 2026 23:00:13 +0530 Subject: [PATCH 02/79] feat: CLIAgentDriver Abstraction --- envs/opencode_env/__init__.py | 19 +- envs/opencode_env/client.py | 2 +- envs/opencode_env/config.py | 4 +- envs/opencode_env/harness.py | 422 +++-------- envs/opencode_env/opencode_runtime.py | 4 +- envs/opencode_env/sandbox/__init__.py | 12 +- envs/opencode_env/sandbox/build_template.py | 5 +- envs/opencode_env/server/app.py | 10 +- envs/opencode_env/server/gradio_ui.py | 95 ++- .../server/opencode_environment.py | 19 +- src/openenv/core/harness/agents/__init__.py | 107 +++ src/openenv/core/harness/agents/base.py | 251 ++++++ src/openenv/core/harness/agents/cli_driver.py | 716 ++++++++++++++++++ src/openenv/core/harness/agents/opencode.py | 191 +++++ 14 files changed, 1467 insertions(+), 390 deletions(-) create mode 100644 src/openenv/core/harness/agents/__init__.py create mode 100644 src/openenv/core/harness/agents/base.py create mode 100644 src/openenv/core/harness/agents/cli_driver.py create mode 100644 src/openenv/core/harness/agents/opencode.py diff --git a/envs/opencode_env/__init__.py b/envs/opencode_env/__init__.py index 17cd145b3..dcd48a01c 100644 --- a/envs/opencode_env/__init__.py +++ b/envs/opencode_env/__init__.py @@ -8,35 +8,30 @@ Two layers in this package: -1. **Harness primitive** — :class:`OpenCodeSessionFactory` / +1. **Harness primitive** -- :class:`OpenCodeSessionFactory` / :class:`OpenCodeSession` / :class:`OpenCodeConfig` / - :class:`E2BSandboxBackend`. Used in-process to drive one rollout - inside an E2B sandbox. See ``harness.py``. + :class:`E2BSandboxBackend`. Built on the generic + :class:`CLIAgentDriver` from ``openenv.core.harness.agents``. -2. **Deployable env** — :class:`OpenCodeEnv` (MCP client) talks to the +2. **Deployable env** -- :class:`OpenCodeEnv` (MCP client) talks to the FastAPI server at ``server/app.py`` over HTTP. Use this when the sandbox + agent live behind an HTTP boundary (e.g. an HF Space). See ``client.py`` and ``server/``. """ from openenv.core.env_server.mcp_types import CallToolAction, ListToolsAction +from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle from .client import OpenCodeEnv from .config import OpenCodeConfig, Provider from .harness import OpenCodeSession, OpenCodeSessionFactory -from .models import ( - CommandResult, - OpenCodeState, - RolloutResult, - RolloutTurn, -) -from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle +from .models import CommandResult, OpenCodeState, RolloutResult, RolloutTurn +from .task import OpenCodeTask try: from openenv.core.harness.sandbox import E2BSandboxBackend except ImportError: # e2b not installed E2BSandboxBackend = None # type: ignore[assignment,misc] -from .task import OpenCodeTask __all__ = [ # Deployed-env client diff --git a/envs/opencode_env/client.py b/envs/opencode_env/client.py index a00afc4e1..52e76e2d5 100644 --- a/envs/opencode_env/client.py +++ b/envs/opencode_env/client.py @@ -51,7 +51,7 @@ def run_rollout( self, *, # Endpoint — pass either the shorthand selector OR explicit fields. - endpoint: str = "", # "vllm" | "openai" | "hf_router" + endpoint: str = "", # "vllm" | "openai" | "hf_router" base_url: str = "", api_key: str = "", model: str = "", diff --git a/envs/opencode_env/config.py b/envs/opencode_env/config.py index 57273b9eb..2b6bae0a2 100644 --- a/envs/opencode_env/config.py +++ b/envs/opencode_env/config.py @@ -34,9 +34,7 @@ class OpenCodeConfig(BaseModel): # --- OpenCode CLI --------------------------------------------------------- opencode_version: str = "latest" - disabled_tools: list[str] = Field( - default_factory=lambda: ["webfetch", "question"] - ) + disabled_tools: list[str] = Field(default_factory=lambda: ["webfetch", "question"]) enabled_tools: list[str] | None = None system_prompt: str | None = None extra_opencode_json: dict[str, Any] = Field(default_factory=dict) diff --git a/envs/opencode_env/harness.py b/envs/opencode_env/harness.py index dc0eb55be..600aafa82 100644 --- a/envs/opencode_env/harness.py +++ b/envs/opencode_env/harness.py @@ -4,42 +4,32 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""OpenCode session factory + session implementation. +"""OpenCode session factory + session — backed by CLIAgentDriver. -Implements the :class:`ResourceSessionFactory` / :class:`ResourceSession` -contracts from ``openenv.core.harness`` (PR #471). The session wraps one -sandbox running the ``opencode`` CLI agent. +This module exposes :class:`OpenCodeSession` and +:class:`OpenCodeSessionFactory` built on top of the generic +:class:`CLIAgentDriver` / :class:`CLIAgentSession` / +:class:`CLIAgentSessionFactory` from ``openenv.core.harness.agents``. -Two operating modes: - - - ``mode="black_box"`` — opencode talks directly to ``config.base_url``. - No proxy, no logprob capture. Use for smoke tests / SFT / eval. - - ``mode="transparent_proxy"`` (default) — an in-sandbox FastAPI proxy - sits between opencode and the upstream LLM. It injects ``logprobs=true`` - on every request and writes per-turn ``(messages, completion_tokens, - per_token_logps)`` to ``proxy_trace.jsonl`` for GRPO consumption. - -Single driver path: opencode is started as a background subprocess via -``opencode run --format json --dangerously-skip-permissions ...`` and we -poll its exit code. The previous ``opencode serve`` driver was removed — -opencode CLI is the only path now. +OpenCode-specific configuration (``opencode.json`` generation, provider +mapping, tool enable/disable) is handled by +:mod:`opencode_env.opencode_runtime` builders wired into the +:data:`OPENCODE_SPEC` via callable hooks. """ from __future__ import annotations -import json -import shlex from pathlib import Path -from typing import Any, Callable, Literal - -from openenv.core.env_server.mcp_types import Tool -from openenv.core.harness import ( - Message, - ResourceSession, - ResourceSessionFactory, - ToolResult, - VerifyResult, +from typing import Any, Literal + +from openenv.core.harness import ResourceSessionFactory +from openenv.core.harness.agents.cli_driver import ( + CLIAgentDriver, + CLIAgentSession, + Verifier, ) +from openenv.core.harness.agents.opencode import OPENCODE_SPEC +from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle from .config import OpenCodeConfig from .opencode_runtime import ( @@ -52,7 +42,6 @@ opencode_config_path, system_prompt_path, ) -from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle from .task import OpenCodeTask @@ -61,28 +50,24 @@ _PROXY_TRACE_PATH = "/home/user/logs/agent/proxy_trace.jsonl" _PROXY_LOG_PATH = "/home/user/logs/agent/proxy.log" -# Where the proxy source lives on disk (in this repo). Uploaded into the -# sandbox at /home/user/proxy/interception.py before each rollout, unless -# the sandbox was created from a template that already has it baked in. _PROXY_SOURCE_PATH = ( Path(__file__).resolve().parents[2] - / "src" / "openenv" / "core" / "harness" / "sandbox" / "interception.py" + / "src" + / "openenv" + / "core" + / "harness" + / "sandbox" + / "interception.py" ) -Verifier = Callable[[SandboxHandle, OpenCodeTask], VerifyResult] - - -class OpenCodeSession(ResourceSession): +class OpenCodeSession(CLIAgentSession): """One live OpenCode rollout inside a sandbox. - The session is created already-running: :meth:`OpenCodeSessionFactory.create` - calls :meth:`start_agent` before returning. Typical usage:: - - session = factory.create(task) - session.wait_for_completion() - result = session.verify([]) - session.close() + Extends :class:`CLIAgentSession` with OpenCode-specific convenience + methods (``fetch_trace``, ``wait_for_completion`` with config-aware + timeout). Fully backward-compatible with code that used the old + ``OpenCodeSession`` API. """ def __init__( @@ -95,100 +80,43 @@ def __init__( base_url_override: str | None = None, proxy_trace_path: str | None = None, proxy_bg_job: BgJob | None = None, + agent_bg_job: BgJob | None = None, ) -> None: - self.sandbox = sandbox - self.config = config - self.task = task - self._verifier = verifier - self._base_url_override = base_url_override - self._bg_job: BgJob | None = None - self._proxy_trace_path = proxy_trace_path - self._proxy_bg_job = proxy_bg_job - - # ------------------------------------------------------------------ - # ResourceSession contract (PR #471) - # ------------------------------------------------------------------ - def initial_messages(self) -> list[Message]: - return [{"role": "user", "content": self.task.instruction}] - - def list_tools(self) -> list[Tool]: - # OpenCode owns its own tool loop — none are exposed to the harness. - return [] - - def call_tool(self, name: str, arguments: dict[str, Any]) -> ToolResult: - return ToolResult( - error=( - "OpenCodeSession does not expose external tool calls; the " - "CLI agent owns its own tool loop." - ) + super().__init__( + spec=OPENCODE_SPEC, + sandbox=sandbox, + task=task, + config=config, + verifier=verifier, + base_url_override=base_url_override, + proxy_trace_path=proxy_trace_path, + proxy_bg_job=proxy_bg_job, + agent_bg_job=agent_bg_job, ) - def verify( - self, - transcript: list[Message], - final_state: Any | None = None, - ) -> VerifyResult: - if self._verifier is None: - return VerifyResult(env_reward=None, done=True) - return self._verifier(self.sandbox, self.task) - - def close(self) -> None: - if self._bg_job is not None: - try: - self._bg_job.kill() - except Exception: - pass - self._bg_job = None - if self._proxy_bg_job is not None: - try: - self._proxy_bg_job.kill() - except Exception: - pass - self._proxy_bg_job = None - self.sandbox.kill() - - # ------------------------------------------------------------------ - # OpenCode-specific session API - # ------------------------------------------------------------------ - def start_agent(self) -> None: - """Launch ``opencode run`` as a background subprocess in the sandbox.""" - if self._bg_job is not None: - return - cmd = build_run_cmd(self.config) - envs = build_env_vars(self.config, base_url_override=self._base_url_override) - self._bg_job = self.sandbox.start_bg(cmd, envs=envs) + def fetch_trace(self) -> str: + """Return the raw ``opencode run`` log (JSONL when ``run_format=json``).""" + return self.sandbox.read_text(agent_log_path(self.config)) def wait_for_completion(self, timeout_s: float | None = None) -> int: """Block until the agent exits, returning its exit code.""" budget = timeout_s if timeout_s is not None else self.config.agent_timeout_s - if self._bg_job is None: + if self._agent_bg_job is None: raise RuntimeError("Agent not started; call start_agent() first.") - return self._bg_job.wait(timeout=budget) + return self._agent_bg_job.wait(timeout=budget) - def fetch_trace(self) -> str: - """Return the raw ``opencode run`` log (JSON-lines when ``run_format=json``).""" - return self.sandbox.read_text(agent_log_path(self.config)) - - def fetch_proxy_trace(self) -> list[dict[str, Any]]: - """Return per-turn proxy-captured records (Mode B only). + def start_agent(self) -> None: + """Launch ``opencode run`` as a background subprocess in the sandbox. - Each entry has ``request``, ``response``, ``completion_tokens``, - ``completion_token_ids``, ``per_token_logps``, ``finish_reason``, - and ``latency_s``. Returns ``[]`` in Mode A. + Provided for backward compatibility — the factory now starts the + agent during ``create()``, so calling this manually is a no-op + if the agent is already running. """ - if self._proxy_trace_path is None: - return [] - try: - content = self.sandbox.read_text(self._proxy_trace_path) - except Exception: - return [] - records: list[dict[str, Any]] = [] - for line in content.splitlines(): - line = line.strip() - if not line: - continue - records.append(json.loads(line)) - return records + if self._agent_bg_job is not None: + return + cmd = build_run_cmd(self.config) + envs = build_env_vars(self.config, base_url_override=self._base_url_override) + self._agent_bg_job = self.sandbox.start_bg(cmd, envs=envs) class OpenCodeSessionFactory(ResourceSessionFactory): @@ -197,6 +125,10 @@ class OpenCodeSessionFactory(ResourceSessionFactory): The factory owns sandbox provisioning, opencode install, config injection, and (Mode B) proxy startup. Each :meth:`create` call returns a fresh sandbox with a running agent. + + Internally delegates to :class:`CLIAgentDriver` for the generic + sandbox lifecycle (readiness probing, install retry, proxy startup). + OpenCode-specific config generation uses ``opencode_runtime`` builders. """ def __init__( @@ -218,6 +150,18 @@ def __init__( self._install_timeout_s = install_timeout_s self._setup_timeout_s = setup_timeout_s + # Build a CLIAgentDriver for the shared lifecycle. + self._driver = CLIAgentDriver( + spec=OPENCODE_SPEC, + sandbox_backend=sandbox_backend, + mode=mode, + install_timeout_s=install_timeout_s, + setup_timeout_s=setup_timeout_s, + proxy_top_logprobs=config.proxy_top_logprobs, + proxy_max_tokens_cap=config.proxy_max_tokens_cap, + proxy_disable_thinking=config.proxy_disable_thinking, + ) + def create( self, task: Any, @@ -225,6 +169,7 @@ def create( episode_id: str | None = None, ) -> OpenCodeSession: import logging + _log = logging.getLogger(__name__) oc_task = OpenCodeTask.coerce(task) @@ -232,17 +177,16 @@ def create( _log.info( "factory.create: creating sandbox timeout=%ds mode=%s", - sandbox_timeout, self._mode, + sandbox_timeout, + self._mode, ) sandbox = self._backend.create( timeout_s=sandbox_timeout, metadata={"episode_id": episode_id} if episode_id else None, ) - sid = ( - getattr(sandbox, "sandbox_id", None) - or getattr(getattr(sandbox, "raw", None), "sandbox_id", "?") - ) + sid = getattr(sandbox, "sandbox_id", "?") _log.info("factory.create: sandbox=%s — bootstrapping…", sid) + try: self._bootstrap_sandbox(sandbox, oc_task) except Exception as exc: @@ -256,18 +200,20 @@ def create( if self._mode == "transparent_proxy": _log.info( "factory.create: starting interception proxy on :%d → %s", - _PROXY_PORT, self._config.base_url, + _PROXY_PORT, + self._config.base_url, ) - proxy_bg_job, base_url_override, proxy_trace_path = self._start_proxy( - sandbox + proxy_bg_job, base_url_override, proxy_trace_path = ( + self._driver._start_proxy( + sandbox, + base_url=self._config.base_url, + api_key=self._config.api_key, + model=self._config.model, + ) ) _log.info("factory.create: proxy up at %s", base_url_override) - # Rewrite opencode.json so opencode points at the proxy. Force - # ``openai_compatible`` so opencode hits ``/v1/chat/completions`` - # (which the proxy serves) rather than provider-specific paths. - from .config import OpenCodeConfig as _OCC - - proxy_cfg = _OCC( + # Rewrite opencode.json so opencode points at the proxy. + proxy_cfg = OpenCodeConfig( **{ **self._config.model_dump(), "provider": "openai_compatible", @@ -292,92 +238,8 @@ def create( return session # ------------------------------------------------------------------ - def _wait_for_sandbox_ready( - self, - sandbox: SandboxHandle, - *, - attempts: int = 15, - delay_s: float = 1.0, - ) -> None: - """Probe the sandbox until ``echo ok`` succeeds. - - E2B (and other backends) sometimes return the handle before the - guest is fully ready. Issue ``echo ok`` with short timeouts until - it succeeds. Returns silently on success; raises ``RuntimeError`` - on prolonged failure. - """ - import time - - last_err = "" - for _ in range(attempts): - try: - r = sandbox.exec("echo ok", timeout=5) - if r.exit_code == 0 and "ok" in (r.stdout or ""): - return - last_err = (r.stderr or r.stdout or "").strip() or f"exit={r.exit_code}" - except Exception as exc: # noqa: BLE001 - last_err = f"{type(exc).__name__}: {exc}" - time.sleep(delay_s) - raise RuntimeError( - f"sandbox did not become ready within {attempts * delay_s:.0f}s " - f"(last error: {last_err})" - ) - - def _exec_with_retry( - self, - sandbox: SandboxHandle, - cmd: str, - *, - timeout: float, - attempts: int = 3, - backoff_s: float = 3.0, - label: str = "cmd", - ): - """Run ``sandbox.exec`` with exponential backoff on transient failure. - - Transient = ``exit_code != 0`` AND empty stderr (SIGKILL / network - blip signature) OR an exception during exec. Final failure is raised - as ``RuntimeError`` carrying the last exit code + stderr. - """ - import time - - last_stdout = "" - last_stderr = "" - last_exit = 0 - for i in range(attempts): - try: - r = sandbox.exec(cmd, timeout=timeout) - if r.exit_code == 0: - return r - last_stdout = r.stdout or "" - last_stderr = r.stderr or "" - last_exit = r.exit_code - if last_stderr.strip(): - break - except Exception as exc: # noqa: BLE001 - last_stderr = f"{type(exc).__name__}: {exc}" - last_exit = -1 - if i + 1 < attempts: - time.sleep(backoff_s * (2**i)) - raise RuntimeError( - f"{label} failed after {attempts} attempts " - f"(exit={last_exit}, stderr={last_stderr!r}, stdout_tail={last_stdout[-400:]!r})" - ) - - def _opencode_already_installed(self, sandbox: SandboxHandle) -> bool: - """Cheap probe — returns True if opencode is on disk in the sandbox. - - Used to skip the slow ``curl install`` step when running against a - prebaked template that already ships opencode. - """ - try: - r = sandbox.exec( - "/home/user/.opencode/bin/opencode --version", - timeout=10, - ) - return r.exit_code == 0 - except Exception: - return False + # Bootstrap — delegates to CLIAgentDriver utilities + # ------------------------------------------------------------------ def _bootstrap_sandbox( self, @@ -387,12 +249,11 @@ def _bootstrap_sandbox( """Install opencode, write config + task files, run optional setup.""" # Stage 1: wait for the sandbox to be responsive. - self._wait_for_sandbox_ready(sandbox) + self._driver._wait_for_sandbox_ready(sandbox) - # Stage 2: install opencode (skipped if a prebaked template already - # has it). curl|bash is flaky — retry with backoff. - if not self._opencode_already_installed(sandbox): - self._exec_with_retry( + # Stage 2: install opencode (skipped if pre-baked). + if not self._driver._agent_already_installed(sandbox): + self._driver._exec_with_retry( sandbox, build_install_cmd(self._config), timeout=self._install_timeout_s, @@ -401,6 +262,7 @@ def _bootstrap_sandbox( label="opencode install", ) + # Stage 3: write opencode.json + task files. sandbox.write_text( opencode_config_path(self._config), build_opencode_json(self._config), @@ -416,8 +278,9 @@ def _bootstrap_sandbox( for remote_path, content in task.upload_files.items(): sandbox.write_text(remote_path, content) + # Stage 4: extra setup if self._config.extra_setup_shell: - self._exec_with_retry( + self._driver._exec_with_retry( sandbox, self._config.extra_setup_shell, timeout=self._setup_timeout_s, @@ -437,95 +300,14 @@ def _start_proxy( self, sandbox: SandboxHandle, ) -> tuple[BgJob, str, str]: - """Install proxy deps + start the proxy as a bg job inside the sandbox. - - Returns ``(proxy_bg_job, base_url_override, proxy_trace_path)``. - Skips the pip install + source-upload steps when the prebaked - template already has them in place. - """ - proxy_already_present = sandbox.exists( - "/home/user/proxy/interception.py" + """Start proxy — delegates to driver.""" + return self._driver._start_proxy( + sandbox, + base_url=self._config.base_url, + api_key=self._config.api_key, + model=self._config.model, ) - if not proxy_already_present: - # Install proxy deps (idempotent on retries). - self._exec_with_retry( - sandbox, - "pip install --quiet 'fastapi>=0.104' 'uvicorn[standard]>=0.24' " - "'httpx>=0.27' 2>&1 | tail -20", - timeout=180, - attempts=3, - backoff_s=2.0, - label="proxy deps install", - ) - # Upload the proxy module into the sandbox. - sandbox.write_text( - "/home/user/proxy/interception.py", - _PROXY_SOURCE_PATH.read_text(), - ) - sandbox.write_text("/home/user/proxy/__init__.py", "") - - proxy_args = [ - "python", - "interception.py", - "--upstream-url", - self._config.base_url, - "--trace", - _PROXY_TRACE_PATH, - "--port", - str(_PROXY_PORT), - "--top-logprobs", - str(self._config.proxy_top_logprobs), - ] - if self._config.proxy_max_tokens_cap is not None: - proxy_args.extend( - ["--max-tokens-cap", str(self._config.proxy_max_tokens_cap)] - ) - if self._config.proxy_disable_thinking: - proxy_args.append("--disable-thinking") - # Force the upstream model id on every forwarded request — opencode's - # internal title-gen call sometimes strips the provider prefix. - if self._config.model: - proxy_args.extend(["--model-override", self._config.model]) - - quoted_proxy_args = " ".join(shlex.quote(arg) for arg in proxy_args) - proxy_cmd = ( - "cd /home/user/proxy && " - f"{quoted_proxy_args} " - f"> {shlex.quote(_PROXY_LOG_PATH)} 2>&1" - ) - proxy_env = {"OPENCODE_UPSTREAM_API_KEY": self._config.api_key} - proxy_job = sandbox.start_bg(proxy_cmd, envs=proxy_env) - - # Wait for the proxy to start listening. Cold uvicorn boot inside - # E2B can take anywhere from <1s to ~30s depending on cache state. - import time - - attempts = 120 - interval_s = 0.5 - for _ in range(attempts): - r = sandbox.exec( - f"curl -sf http://127.0.0.1:{_PROXY_PORT}/healthz", - timeout=5, - ) - if r.exit_code == 0: - break - time.sleep(interval_s) - else: - log = "" - try: - log = sandbox.read_text(_PROXY_LOG_PATH) - except Exception: - pass - proxy_job.kill() - raise RuntimeError( - f"proxy did not start within {attempts * interval_s:.0f}s. " - f"log:\n{log[-2000:]}" - ) - - base_url_override = f"http://127.0.0.1:{_PROXY_PORT}/v1" - return proxy_job, base_url_override, _PROXY_TRACE_PATH - __all__ = [ "OpenCodeSession", diff --git a/envs/opencode_env/opencode_runtime.py b/envs/opencode_env/opencode_runtime.py index 07fd5322d..75fed41e3 100644 --- a/envs/opencode_env/opencode_runtime.py +++ b/envs/opencode_env/opencode_runtime.py @@ -111,7 +111,9 @@ def build_run_cmd(config: OpenCodeConfig) -> str: ).strip() -def build_env_vars(config: OpenCodeConfig, *, base_url_override: str | None = None) -> dict[str, str]: +def build_env_vars( + config: OpenCodeConfig, *, base_url_override: str | None = None +) -> dict[str, str]: """Return env vars to set on the OpenCode process. When a proxy is wrapping ``config.base_url`` the factory passes the proxy's diff --git a/envs/opencode_env/sandbox/__init__.py b/envs/opencode_env/sandbox/__init__.py index a3496a2b1..8a2477104 100644 --- a/envs/opencode_env/sandbox/__init__.py +++ b/envs/opencode_env/sandbox/__init__.py @@ -4,13 +4,9 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Sandbox backends — re-exported from ``openenv.core.harness.sandbox``. +"""Sandbox backends live in ``openenv.core.harness.sandbox``. -The canonical source for sandbox protocols and implementations now lives in -``src/openenv/core/harness/sandbox/``. This package re-exports everything -so that ``from opencode_env.sandbox import ...`` keeps working, but all new -code should import from ``openenv.core.harness.sandbox`` directly. +This package exists only for the ``build_template`` helper used by E2B +template builds. Import sandbox protocols and backends from +``openenv.core.harness.sandbox`` directly. """ - -from openenv.core.harness.sandbox import * # noqa: F401,F403 -from openenv.core.harness.sandbox import __all__ # noqa: F401 diff --git a/envs/opencode_env/sandbox/build_template.py b/envs/opencode_env/sandbox/build_template.py index 084a95e64..6e0ba4f75 100644 --- a/envs/opencode_env/sandbox/build_template.py +++ b/envs/opencode_env/sandbox/build_template.py @@ -41,7 +41,7 @@ import sys from pathlib import Path -from e2b import Template, default_build_logger +from e2b import default_build_logger, Template _REPO_ROOT = Path(__file__).resolve().parents[3] @@ -128,8 +128,7 @@ def main(argv: list[str] | None = None) -> int: print("ERROR: E2B_API_KEY required.", file=sys.stderr) return 2 - print(f"Building template '{args.name}' " - f"(proxy source: {_PROXY_SOURCE})") + print(f"Building template '{args.name}' (proxy source: {_PROXY_SOURCE})") print(f"Skip cache: {args.skip_cache}") print() diff --git a/envs/opencode_env/server/app.py b/envs/opencode_env/server/app.py index 200c7f2d7..0757ef229 100644 --- a/envs/opencode_env/server/app.py +++ b/envs/opencode_env/server/app.py @@ -56,19 +56,13 @@ def _load_env_file() -> None: try: from openenv.core.env_server.http_server import create_app - from openenv.core.env_server.mcp_types import ( - CallToolAction, - CallToolObservation, - ) + from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation from .gradio_ui import opencode_gradio_builder from .opencode_environment import OpenCodeEnvironment except ImportError: # pragma: no cover from openenv.core.env_server.http_server import create_app - from openenv.core.env_server.mcp_types import ( - CallToolAction, - CallToolObservation, - ) + from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation from server.gradio_ui import opencode_gradio_builder # type: ignore from server.opencode_environment import OpenCodeEnvironment # type: ignore diff --git a/envs/opencode_env/server/gradio_ui.py b/envs/opencode_env/server/gradio_ui.py index 79a696d75..d1ee6e403 100644 --- a/envs/opencode_env/server/gradio_ui.py +++ b/envs/opencode_env/server/gradio_ui.py @@ -31,10 +31,14 @@ import gradio as gr try: - from .catalog import ENDPOINT_KINDS, catalog_summary, resolve_endpoint + from .catalog import catalog_summary, ENDPOINT_KINDS, resolve_endpoint from .opencode_environment import OpenCodeEnvironment except ImportError: # pragma: no cover - from server.catalog import ENDPOINT_KINDS, catalog_summary, resolve_endpoint # type: ignore + from server.catalog import ( # type: ignore + catalog_summary, + ENDPOINT_KINDS, + resolve_endpoint, + ) from server.opencode_environment import OpenCodeEnvironment # type: ignore @@ -144,7 +148,9 @@ def _command_rows(items: list[dict[str, Any]]) -> list[list[str]]: cmd if len(cmd) <= 80 else cmd[:77] + "...", str(it.get("exit_code", "")), f"{it.get('duration_s', 0):.2f}s", - (it.get("stderr") or "").splitlines()[-1][:80] if it.get("exit_code") else "", + (it.get("stderr") or "").splitlines()[-1][:80] + if it.get("exit_code") + else "", ] ) return rows @@ -175,7 +181,8 @@ def _logprobs_md(turns: list[dict[str, Any]]) -> str: finishes[f] = finishes.get(f, 0) + 1 if finishes: lines.append( - "**finish_reasons**: " + " ".join(f"`{k}={v}`" for k, v in finishes.items()) + "**finish_reasons**: " + + " ".join(f"`{k}={v}`" for k, v in finishes.items()) ) productive_rows = [t for t in turns if t.get("completion_tokens")] if productive_rows: @@ -249,12 +256,12 @@ def _catalog_banner() -> str: def opencode_gradio_builder( - web_manager, # noqa: ARG001 (unused: we instantiate the env directly) - action_fields, # noqa: ARG001 - metadata, # noqa: ARG001 - is_chat_env, # noqa: ARG001 + web_manager, # noqa: ARG001 (unused: we instantiate the env directly) + action_fields, # noqa: ARG001 + metadata, # noqa: ARG001 + is_chat_env, # noqa: ARG001 title, - quick_start_md, # noqa: ARG001 + quick_start_md, # noqa: ARG001 ) -> gr.Blocks: """Build the opencode_env console. @@ -355,7 +362,12 @@ def _worker(): # First yield: announce we've started. Empty result panels. yield ( f"### running…\n\n_endpoint=`{resolved.kind}` model=`{resolved.model}` mode=`{mode}`_", - [], [], "", "", "", {}, + [], + [], + "", + "", + "", + {}, ) status_lines: list[tuple[float, str]] = [] @@ -374,7 +386,9 @@ def _worker(): # Render the live status pane. elapsed = time.time() - t_start - md = _live_status_md(resolved.kind, resolved.model, mode, elapsed, status_lines) + md = _live_status_md( + resolved.kind, resolved.model, mode, elapsed, status_lines + ) yield (md, [], [], "", "", "", {}) # Drain any final messages still in the queue. @@ -390,9 +404,17 @@ def _worker(): err = result_holder.get("error", "unknown error") yield ( f"### error\n\n```\n{err}\n```", - [], [], "", "", - _live_status_md(resolved.kind, resolved.model, mode, - time.time() - t_start, status_lines), + [], + [], + "", + "", + _live_status_md( + resolved.kind, + resolved.model, + mode, + time.time() - t_start, + status_lines, + ), {"error": err}, ) return @@ -406,8 +428,13 @@ def _worker(): _logprobs_md(result.get("proxy_turns") or []), ( f"### live phase log\n\n" - + _live_status_md(resolved.kind, resolved.model, mode, - time.time() - t_start, status_lines) + + _live_status_md( + resolved.kind, + resolved.model, + mode, + time.time() - t_start, + status_lines, + ) + f"\n\n### agent log (tail)\n```\n{result.get('agent_log_tail', '')[:4000]}\n```\n\n" f"### proxy log (tail)\n```\n{result.get('proxy_log_tail', '')[:4000]}\n```" ), @@ -436,17 +463,21 @@ def apply_preset(name: str) -> tuple[str, str, str]: scale=1, ) model = gr.Textbox( - label="Model (blank → catalog default)", placeholder="gpt-4o-mini", + label="Model (blank → catalog default)", + placeholder="gpt-4o-mini", scale=2, ) with gr.Row(): base_url = gr.Textbox( label="Base URL (blank → env / catalog default)", - placeholder="https://api.openai.com/v1", scale=2, + placeholder="https://api.openai.com/v1", + scale=2, ) api_key = gr.Textbox( label="API key (blank → server env var)", - placeholder="(server env)", type="password", scale=1, + placeholder="(server env)", + type="password", + scale=1, ) instruction = gr.Textbox( @@ -536,14 +567,28 @@ def apply_preset(name: str) -> tuple[str, str, str]: run_btn.click( fn=run, inputs=[ - endpoint, model, base_url, api_key, - instruction, setup_text, verify_text, - mode, disable_thinking, template, - max_tokens_cap, top_logprobs, agent_timeout_s, + endpoint, + model, + base_url, + api_key, + instruction, + setup_text, + verify_text, + mode, + disable_thinking, + template, + max_tokens_cap, + top_logprobs, + agent_timeout_s, ], outputs=[ - summary_md, setup_table, verify_table, - files_md, logprobs_md, logs_md, raw_json, + summary_md, + setup_table, + verify_table, + files_md, + logprobs_md, + logs_md, + raw_json, ], ) diff --git a/envs/opencode_env/server/opencode_environment.py b/envs/opencode_env/server/opencode_environment.py index 07f0d69ed..638dd5473 100644 --- a/envs/opencode_env/server/opencode_environment.py +++ b/envs/opencode_env/server/opencode_environment.py @@ -189,9 +189,7 @@ def reset( reward=None, metadata={ "status": "ready", - "message": ( - "opencode_env ready. Call run_rollout(...) with a task." - ), + "message": ("opencode_env ready. Call run_rollout(...) with a task."), }, ) @@ -399,8 +397,12 @@ def _emit(msg: str) -> None: result.error = f"{type(exc).__name__}: {exc}" _emit(f"ERROR: {result.error}") if session is not None: - result.proxy_log_tail = self._safe_read(session.sandbox, PROXY_LOG)[-2000:] - result.agent_log_tail = self._safe_read(session.sandbox, AGENT_LOG)[-2000:] + result.proxy_log_tail = self._safe_read(session.sandbox, PROXY_LOG)[ + -2000: + ] + result.agent_log_tail = self._safe_read(session.sandbox, AGENT_LOG)[ + -2000: + ] finally: if session is not None: try: @@ -450,9 +452,7 @@ def _read_reward(self, sandbox: Any) -> float | None: except ValueError: return None - def _collect_files( - self, sandbox: Any - ) -> tuple[dict[str, str], list[str]]: + def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: listing = sandbox.exec( f"find {WORKDIR} -maxdepth 2 -type f -size -64k 2>/dev/null | head -32", timeout=10, @@ -491,7 +491,8 @@ def _collect_proxy_turns(self, session: Any) -> list[Any]: completion_tokens=list(rec.get("completion_tokens") or []), completion_token_ids=list(rec.get("completion_token_ids") or []), per_token_logps=[ - float(x) for x in (rec.get("per_token_logps") or []) + float(x) + for x in (rec.get("per_token_logps") or []) if x is not None ], latency_s=float(rec.get("latency_s") or 0.0), diff --git a/src/openenv/core/harness/agents/__init__.py b/src/openenv/core/harness/agents/__init__.py new file mode 100644 index 000000000..8ef31976b --- /dev/null +++ b/src/openenv/core/harness/agents/__init__.py @@ -0,0 +1,107 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Agent registry and public API for CLI-based agentic harnesses. + +The registry maps agent names (``"opencode"``, ``"claude-code"``, etc.) to +their :class:`CLIAgentSpec` declarations. Each agent module registers itself +via :func:`register_agent` at import time. + +Usage:: + + from openenv.core.harness.agents import get_agent_spec, list_agents + + spec = get_agent_spec("opencode") + print(list_agents()) # ["opencode"] +""" + +from __future__ import annotations + +from .base import ( + AgentConfig, + AgentEvent, + AgentTask, + ArtifactSpec, + CLIAgentSpec, + MCPConfigSpec, +) + +# Registry + +_REGISTRY: dict[str, CLIAgentSpec] = {} + + +def register_agent(spec: CLIAgentSpec) -> None: + """Register a :class:`CLIAgentSpec` under ``spec.name``. + + Raises :class:`ValueError` if the name is already registered with a + *different* spec object (re-registering the same object is a no-op, + which makes ``importlib.reload`` safe). + """ + existing = _REGISTRY.get(spec.name) + if existing is not None and existing is not spec: + raise ValueError( + f"Agent {spec.name!r} is already registered. " + "Use a unique name or call unregister_agent() first." + ) + _REGISTRY[spec.name] = spec + + +def unregister_agent(name: str) -> CLIAgentSpec | None: + """Remove a registered agent spec, returning it (or ``None``).""" + return _REGISTRY.pop(name, None) + + +def get_agent_spec(name: str) -> CLIAgentSpec: + """Look up a registered agent spec by name. + + Raises :class:`KeyError` if not found. To trigger auto-registration of + built-in agents, import the specific module first (e.g. + ``import openenv.core.harness.agents.opencode``). + """ + if name not in _REGISTRY: + # Auto-import built-in agent modules to trigger registration. + _auto_import(name) + try: + return _REGISTRY[name] + except KeyError: + available = ", ".join(sorted(_REGISTRY)) or "(none)" + raise KeyError( + f"Unknown agent {name!r}. Registered agents: {available}" + ) from None + + +def list_agents() -> list[str]: + """Return sorted names of all registered agents.""" + return sorted(_REGISTRY) + + +def _auto_import(name: str) -> None: + """Try to import the built-in module for ``name`` to trigger registration.""" + # Map agent names to module names (handles hyphens). + module_name = name.replace("-", "_") + try: + __import__(f"openenv.core.harness.agents.{module_name}", fromlist=["_"]) + except ImportError: + pass + + +# Convenience re-exports + +__all__ = [ + # Registry + "get_agent_spec", + "list_agents", + "register_agent", + "unregister_agent", + # Base types + "AgentConfig", + "AgentEvent", + "AgentTask", + "ArtifactSpec", + "CLIAgentSpec", + "MCPConfigSpec", +] diff --git a/src/openenv/core/harness/agents/base.py b/src/openenv/core/harness/agents/base.py new file mode 100644 index 000000000..145d3001e --- /dev/null +++ b/src/openenv/core/harness/agents/base.py @@ -0,0 +1,251 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Agent spec and event protocols for CLI-based agentic harnesses. + +Defines the declarative :class:`CLIAgentSpec` data model that captures +*everything* a CLI harness needs — install commands, file uploads, MCP +config format, environment variables, artifacts to collect, and three +small callables (command builder, MCP config builder, event parser). + +The :class:`CLIAgentDriver` reads these fields mechanically without knowing +anything about the specific agent. Adding a new agent is filling in a +dataclass, not writing driver code. + +Pattern borrowed from `verifiers `_ +(Prime Intellect), where OpenCode, MiniSWEAgent, Pi, and RLM all express +their differences through constructor data passed to ``CLIHarness.__init__()``. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any, Callable, Literal, Protocol + + +# MCP config injection + + +@dataclass(frozen=True) +class MCPConfigSpec: + """How a harness discovers MCP tools. + + ``method`` controls how the driver injects MCP server configuration: + + - ``"config_file"`` — write a JSON file at ``path_template`` (e.g. + ``"{workdir}/mcp.json"``). The template receives ``{workdir}`` + and ``{home}`` substitutions at runtime. + - ``"cli_flags"`` — the driver passes MCP configuration via CLI + flags built by :attr:`CLIAgentSpec.build_command`. + - ``"settings_file"`` — write into a global settings file (e.g. + Gemini's ``~/.gemini/settings.json``). + """ + + method: Literal["config_file", "cli_flags", "settings_file"] + path_template: str | None = None + + +# Artifacts + + +@dataclass(frozen=True) +class ArtifactSpec: + """Declares a file to collect from the sandbox after the agent exits. + + The driver iterates :attr:`CLIAgentSpec.artifacts` and calls + ``sandbox.read_text(spec.path)`` for each entry. No per-agent collection + methods needed — the spec declares *what* to collect, the driver collects + it. + """ + + path: str + format: Literal["text", "json", "jsonl"] = "text" + optional: bool = True + + +# Agent events (normalized across harnesses) + + +@dataclass +class AgentEvent: + """Normalized event from any CLI harness's stdout. + + The :attr:`CLIAgentSpec.parse_events` callable converts raw JSONL lines + into these events so the driver can log and observe the agent's progress + without knowing which agent is running. + """ + + type: Literal[ + "assistant", + "tool_call", + "tool_result", + "reasoning", + "error", + "done", + ] + data: dict[str, Any] = field(default_factory=dict) + raw: str = "" + + +# Task protocol + + +class AgentTask(Protocol): + """Minimal interface a task must satisfy for the CLI agent driver.""" + + @property + def instruction(self) -> str: ... + + @property + def setup_shell(self) -> str | None: ... + + @property + def upload_files(self) -> dict[str, str]: ... + + @property + def metadata(self) -> dict[str, Any]: ... + + +# Agent config protocol + + +class AgentConfig(Protocol): + """Minimal interface a config must satisfy for the CLI agent driver. + + This is intentionally thin — concrete configs like :class:`OpenCodeConfig` + carry much more, but the generic driver only accesses these. + """ + + @property + def base_url(self) -> str: ... + + @property + def api_key(self) -> str: ... + + @property + def model(self) -> str: ... + + @property + def agent_timeout_s(self) -> float: ... + + +# CLIAgentSpec — the core declarative data model + + +@dataclass +class CLIAgentSpec: + """Declarative specification for a CLI-based agentic harness. + + Following the pattern established by verifiers' ``CLIHarness`` (Prime + Intellect), as much per-agent knowledge as possible is expressed as + *data* rather than imperative code. The :class:`CLIAgentDriver` + iterates these fields mechanically — it never needs to know what + ``"pi"`` or ``"claude-code"`` means. + + Three callables cover the remaining agent-specific logic that can't + be expressed as pure data: + + - :attr:`build_command` — constructs the CLI argv + - :attr:`build_mcp_config` — serializes MCP server configuration + - :attr:`parse_events` — converts raw stdout lines to :class:`AgentEvent` + + Everything else — file uploads, env vars, install scripts, artifact + collection — is pure data. + """ + + name: str + """Unique identifier: ``"opencode"``, ``"claude-code"``, ``"codex"``, etc.""" + + install_check_cmd: list[str] + """Command to probe whether the agent is already installed. + + Example: ``["claude", "--version"]`` + """ + + base_command: list[str] + """Base CLI invocation (before task-specific flags). + + Example: ``["claude", "--print", "--output-format", "stream-json"]`` + """ + + mcp_config: MCPConfigSpec + """How MCP tool configuration is injected.""" + + supports_logprob_proxy: bool = True + """Whether this agent can be routed through the interception proxy.""" + + default_timeout_s: float = 600.0 + """Default per-rollout timeout in seconds.""" + + setup: str | list[str] | None = None + """Shell command(s) to install the agent CLI inside the sandbox. + + Run once after the sandbox is created, before any files are written. + Skipped when ``install_check_cmd`` succeeds (pre-baked template). + Can be a single string or a list of strings executed in order. + """ + + files: dict[str, str | Callable] | None = None + """Files to upload into the sandbox before the agent starts. + + Keys are absolute sandbox paths. Values are either literal strings or + callables ``(task, config) -> str`` resolved at rollout time. + """ + + artifacts: dict[str, ArtifactSpec] | None = None + """Files to collect from the sandbox after the agent exits. + + The driver iterates this dict and calls ``sandbox.read_text(spec.path)`` + for each entry. + """ + + env: dict[str, str] | None = None + """Environment variables for the agent process. + + Values can contain ``{model}``, ``{base_url}``, ``{api_key}`` placeholders + resolved from the rollout config at runtime. + """ + + build_command: Callable[..., str] | None = None + """``(spec, config, task, mcp_config_path) -> str`` + + Build the full shell command line for launching the agent. Returns a + string (not a list) because sandbox ``start_bg`` / ``exec`` take shell + strings. + """ + + build_mcp_config: Callable[..., str] | None = None + """``(spec, tools, workdir) -> str`` + + Serialize MCP server configuration in the format the agent expects. + Returns the file content (for ``config_file``/``settings_file`` methods) + or empty string (for ``cli_flags``, where the command builder handles it). + """ + + parse_events: Callable[[str], AgentEvent | None] | None = None + """``(line: str) -> AgentEvent | None`` + + Parse one line of the agent's stdout into a normalized event. + Return ``None`` for lines that are not parseable events. + """ + + build_env_vars: Callable[..., dict[str, str]] | None = None + """``(spec, config) -> dict[str, str]`` + + Optional override for env var construction. When provided, this is + called *instead of* resolving placeholders in :attr:`env`. Prefer + the declarative :attr:`env` dict for new agents. + """ + + +__all__ = [ + "AgentConfig", + "AgentEvent", + "AgentTask", + "ArtifactSpec", + "CLIAgentSpec", + "MCPConfigSpec", +] diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py new file mode 100644 index 000000000..8e8179889 --- /dev/null +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -0,0 +1,716 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Shared CLI agent driver, session, and session factory. + +The :class:`CLIAgentDriver` factors out the common 70% of CLI harness +lifecycle — sandbox creation, MCP config injection, interception proxy +setup, subprocess management, and result collection. + +It is **fully generic**: it reads the :class:`CLIAgentSpec`'s declarative +data fields and executes them mechanically. No per-agent code lives here. + +The :class:`CLIAgentSession` implements :class:`ResourceSession` and +the :class:`CLIAgentSessionFactory` implements :class:`ResourceSessionFactory`, +so the CLI agent driver integrates seamlessly with the existing harness +runtime from PR #603. +""" + +from __future__ import annotations + +import json +import logging +import shlex +import time +from pathlib import Path +from typing import Any, Callable, Literal + +from openenv.core.env_server.mcp_types import Tool +from openenv.core.harness import ( + Message, + ResourceSession, + ResourceSessionFactory, + ToolResult, + VerifyResult, +) +from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle + +from .base import CLIAgentSpec + + +_log = logging.getLogger(__name__) + +# Interception proxy defaults +_PROXY_PORT = 7000 +_PROXY_TRACE_PATH = "/home/user/logs/agent/proxy_trace.jsonl" +_PROXY_LOG_PATH = "/home/user/logs/agent/proxy.log" + +# Where the proxy source lives on disk. Uploaded into sandboxes that don't +# already have it baked in. +_PROXY_SOURCE_PATH = Path(__file__).resolve().parents[1] / "sandbox" / "interception.py" + +# Verifier type — same as opencode_env's Verifier alias +Verifier = Callable[..., VerifyResult] + + +# CLIAgentSession + + +class CLIAgentSession(ResourceSession): + """Per-rollout session wrapping one sandbox with one running agent CLI. + + The session is created already-running: :meth:`CLIAgentSessionFactory.create` + launches the agent before returning. Typical usage:: + + session = factory.create(task) + session.wait_for_completion() + result = session.verify([]) + session.close() + """ + + def __init__( + self, + *, + spec: CLIAgentSpec, + sandbox: SandboxHandle, + task: Any, + config: Any, + verifier: Verifier | None = None, + base_url_override: str | None = None, + proxy_trace_path: str | None = None, + proxy_bg_job: BgJob | None = None, + agent_bg_job: BgJob | None = None, + ) -> None: + self.spec = spec + self.sandbox = sandbox + self.task = task + self.config = config + self._verifier = verifier + self._base_url_override = base_url_override + self._proxy_trace_path = proxy_trace_path + self._proxy_bg_job = proxy_bg_job + self._agent_bg_job = agent_bg_job + + # ResourceSession contract + + def initial_messages(self) -> list[Message]: + instruction = ( + self.task.instruction + if hasattr(self.task, "instruction") + else str(self.task) + ) + return [{"role": "user", "content": instruction}] + + def list_tools(self) -> list[Tool]: + # CLI agents own their own tool loop — none are exposed to the harness. + return [] + + def call_tool(self, name: str, arguments: dict[str, Any]) -> ToolResult: + return ToolResult( + error=( + f"{self.spec.name} session does not expose external tool calls; " + "the CLI agent owns its own tool loop." + ) + ) + + def verify( + self, + transcript: list[Message], + final_state: Any | None = None, + ) -> VerifyResult: + if self._verifier is None: + return VerifyResult(env_reward=None, done=True) + return self._verifier(self.sandbox, self.task) + + def close(self) -> None: + if self._agent_bg_job is not None: + try: + self._agent_bg_job.kill() + except Exception: + pass + self._agent_bg_job = None + if self._proxy_bg_job is not None: + try: + self._proxy_bg_job.kill() + except Exception: + pass + self._proxy_bg_job = None + self.sandbox.kill() + + # CLI-agent-specific API + + def wait_for_completion(self, timeout_s: float | None = None) -> int: + """Block until the agent exits, returning its exit code.""" + budget = timeout_s if timeout_s is not None else self.spec.default_timeout_s + if hasattr(self.config, "agent_timeout_s"): + budget = timeout_s if timeout_s is not None else self.config.agent_timeout_s + if self._agent_bg_job is None: + raise RuntimeError("Agent not started.") + return self._agent_bg_job.wait(timeout=budget) + + def collect_artifacts(self) -> dict[str, Any]: + """Collect all artifacts declared in ``spec.artifacts`` from the sandbox. + + Returns a dict keyed by artifact name. Missing optional artifacts are + silently skipped. + """ + result: dict[str, Any] = {} + if not self.spec.artifacts: + return result + for name, artifact_spec in self.spec.artifacts.items(): + try: + content = self.sandbox.read_text(artifact_spec.path) + if artifact_spec.format == "json": + result[name] = json.loads(content) + elif artifact_spec.format == "jsonl": + result[name] = [ + json.loads(line) + for line in content.splitlines() + if line.strip() + ] + else: + result[name] = content + except Exception: + if not artifact_spec.optional: + raise + _log.debug( + "Optional artifact %r (%s) not found, skipping", + name, + artifact_spec.path, + ) + return result + + def fetch_proxy_trace(self) -> list[dict[str, Any]]: + """Return per-turn proxy-captured records (transparent_proxy mode only). + + Each entry has ``request``, ``response``, ``completion_tokens``, + ``completion_token_ids``, ``per_token_logps``, ``finish_reason``, + and ``latency_s``. Returns ``[]`` in black_box mode. + """ + if self._proxy_trace_path is None: + return [] + try: + content = self.sandbox.read_text(self._proxy_trace_path) + except Exception: + return [] + records: list[dict[str, Any]] = [] + for line in content.splitlines(): + line = line.strip() + if not line: + continue + records.append(json.loads(line)) + return records + + +# CLIAgentDriver — shared lifecycle + + +class CLIAgentDriver: + """Shared driver for all CLI-based agentic harnesses. + + Implements the common lifecycle: + + 1. Create sandbox (via :class:`SandboxBackend`) + 2. Wait for sandbox ready (``echo ok`` probe) + 3. Install agent CLI — run ``spec.setup`` commands (skipped if + ``spec.install_check_cmd`` succeeds, i.e. pre-baked template) + 4. Upload ``spec.files`` into the sandbox + 5. Write MCP config (via ``spec.build_mcp_config``) + 6. Set environment variables from ``spec.env`` (with placeholder + resolution) + 7. Optionally start interception proxy (transparent_proxy mode) + 8. Build CLI command (via ``spec.build_command``) + 9. Launch agent as bg process + 10. Return a :class:`CLIAgentSession` + """ + + def __init__( + self, + spec: CLIAgentSpec, + sandbox_backend: SandboxBackend, + mode: Literal["black_box", "transparent_proxy"] = "black_box", + *, + install_timeout_s: int = 240, + setup_timeout_s: int = 300, + proxy_top_logprobs: int = 5, + proxy_max_tokens_cap: int | None = 16384, + proxy_disable_thinking: bool = False, + ) -> None: + if mode not in {"black_box", "transparent_proxy"}: + raise ValueError(f"Unknown mode: {mode!r}") + self.spec = spec + self.sandbox_backend = sandbox_backend + self.mode = mode + self._install_timeout_s = install_timeout_s + self._setup_timeout_s = setup_timeout_s + self._proxy_top_logprobs = proxy_top_logprobs + self._proxy_max_tokens_cap = proxy_max_tokens_cap + self._proxy_disable_thinking = proxy_disable_thinking + + def create_session( + self, + task: Any, + config: Any, + *, + verifier: Verifier | None = None, + seed: int | None = None, + episode_id: str | None = None, + ) -> CLIAgentSession: + """Create a fully bootstrapped session with a running agent. + + This is the main entry point. It: + 1. Creates a sandbox + 2. Bootstraps it (install agent, upload files, write MCP config) + 3. Optionally starts the interception proxy + 4. Launches the agent subprocess + 5. Returns a ready-to-use :class:`CLIAgentSession` + """ + timeout_s = ( + config.agent_timeout_s + if hasattr(config, "agent_timeout_s") + else self.spec.default_timeout_s + ) + sandbox_timeout = int(timeout_s) + 300 + + _log.info( + "%s driver: creating sandbox timeout=%ds mode=%s", + self.spec.name, + sandbox_timeout, + self.mode, + ) + sandbox = self.sandbox_backend.create( + timeout_s=sandbox_timeout, + metadata={"episode_id": episode_id} if episode_id else None, + ) + sid = getattr(sandbox, "sandbox_id", "?") + _log.info("%s driver: sandbox=%s — bootstrapping…", self.spec.name, sid) + + try: + self._bootstrap_sandbox(sandbox, task, config) + except Exception as exc: + _log.error("%s driver: bootstrap failed: %r", self.spec.name, exc) + sandbox.kill() + raise + + base_url_override: str | None = None + proxy_trace_path: str | None = None + proxy_bg_job: BgJob | None = None + + if self.mode == "transparent_proxy": + base_url = config.base_url if hasattr(config, "base_url") else "" + api_key = config.api_key if hasattr(config, "api_key") else "intercepted" + model = config.model if hasattr(config, "model") else "" + + _log.info( + "%s driver: starting interception proxy on :%d → %s", + self.spec.name, + _PROXY_PORT, + base_url, + ) + proxy_bg_job, base_url_override, proxy_trace_path = self._start_proxy( + sandbox, + base_url=base_url, + api_key=api_key, + model=model, + ) + _log.info("%s driver: proxy up at %s", self.spec.name, base_url_override) + + agent_bg_job = self._start_agent( + sandbox, + task, + config, + base_url_override=base_url_override, + ) + + return CLIAgentSession( + spec=self.spec, + sandbox=sandbox, + task=task, + config=config, + verifier=verifier, + base_url_override=base_url_override, + proxy_trace_path=proxy_trace_path, + proxy_bg_job=proxy_bg_job, + agent_bg_job=agent_bg_job, + ) + + # Bootstrap stages + + def _bootstrap_sandbox( + self, + sandbox: SandboxHandle, + task: Any, + config: Any, + ) -> None: + """Install agent, upload files, write MCP config.""" + + # Stage 1: wait for sandbox readiness + self._wait_for_sandbox_ready(sandbox) + + # Stage 2: install agent CLI (skip if pre-baked) + if not self._agent_already_installed(sandbox): + self._install_agent(sandbox) + + # Stage 3: upload spec.files + self._upload_files(sandbox, task, config) + + # Stage 4: write MCP config (if the spec provides a builder) + self._write_mcp_config(sandbox, config) + + # Stage 5: run task.setup_shell if present + setup_shell = task.setup_shell if hasattr(task, "setup_shell") else None + if setup_shell: + r = sandbox.exec(setup_shell, timeout=self._setup_timeout_s) + if r.exit_code != 0: + raise RuntimeError( + f"task.setup_shell failed ({r.exit_code}): {r.stderr}" + ) + + def _wait_for_sandbox_ready( + self, + sandbox: SandboxHandle, + *, + attempts: int = 15, + delay_s: float = 1.0, + ) -> None: + """Probe sandbox until ``echo ok`` succeeds.""" + last_err = "" + for _ in range(attempts): + try: + r = sandbox.exec("echo ok", timeout=5) + if r.exit_code == 0 and "ok" in (r.stdout or ""): + return + last_err = (r.stderr or r.stdout or "").strip() or f"exit={r.exit_code}" + except Exception as exc: + last_err = f"{type(exc).__name__}: {exc}" + time.sleep(delay_s) + raise RuntimeError( + f"sandbox did not become ready within {attempts * delay_s:.0f}s " + f"(last error: {last_err})" + ) + + def _agent_already_installed(self, sandbox: SandboxHandle) -> bool: + """Check if the agent CLI is already available in the sandbox.""" + cmd = " ".join(shlex.quote(c) for c in self.spec.install_check_cmd) + try: + r = sandbox.exec(cmd, timeout=10) + return r.exit_code == 0 + except Exception: + return False + + def _install_agent(self, sandbox: SandboxHandle) -> None: + """Run ``spec.setup`` commands to install the agent CLI.""" + if self.spec.setup is None: + raise RuntimeError( + f"Agent {self.spec.name!r} is not installed in the sandbox " + "and no setup commands are provided in the spec." + ) + commands = ( + [self.spec.setup] if isinstance(self.spec.setup, str) else self.spec.setup + ) + for cmd in commands: + self._exec_with_retry( + sandbox, + cmd, + timeout=self._install_timeout_s, + attempts=3, + backoff_s=3.0, + label=f"{self.spec.name} install", + ) + + def _upload_files( + self, + sandbox: SandboxHandle, + task: Any, + config: Any, + ) -> None: + """Upload ``spec.files`` into the sandbox, resolving callables.""" + if not self.spec.files: + return + for path, content_or_fn in self.spec.files.items(): + if callable(content_or_fn): + content = content_or_fn(task, config) + else: + content = content_or_fn + if content is not None: + sandbox.write_text(path, content) + + # Also upload task.upload_files if the task has them. + upload_files = task.upload_files if hasattr(task, "upload_files") else {} + for path, content in upload_files.items(): + sandbox.write_text(path, content) + + def _write_mcp_config( + self, + sandbox: SandboxHandle, + config: Any, + ) -> None: + """Write MCP configuration using the spec's builder.""" + if self.spec.build_mcp_config is None: + return + if ( + self.spec.mcp_config.method == "config_file" + and self.spec.mcp_config.path_template + ): + workdir = ( + config.sandbox_home + "/workdir" + if hasattr(config, "sandbox_home") + else "/home/user/workdir" + ) + home = ( + config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" + ) + mcp_path = self.spec.mcp_config.path_template.format( + workdir=workdir, + home=home, + ) + mcp_content = self.spec.build_mcp_config(self.spec, [], workdir) + sandbox.write_text(mcp_path, mcp_content) + + # Agent launch + + def _start_agent( + self, + sandbox: SandboxHandle, + task: Any, + config: Any, + *, + base_url_override: str | None = None, + ) -> BgJob: + """Build CLI command, resolve env vars, and launch as bg process.""" + # Build command via spec hook + if self.spec.build_command is not None: + cmd = self.spec.build_command(self.spec, config, task, None) + else: + cmd = " ".join(shlex.quote(c) for c in self.spec.base_command) + + # Resolve environment variables + envs = self._resolve_env_vars(config, base_url_override=base_url_override) + + _log.info("%s driver: launching agent", self.spec.name) + return sandbox.start_bg(cmd, envs=envs) + + def _resolve_env_vars( + self, + config: Any, + *, + base_url_override: str | None = None, + ) -> dict[str, str]: + """Build the env var dict for the agent process. + + If ``spec.build_env_vars`` is provided, delegate to it. + Otherwise resolve ``{placeholder}`` substitutions in ``spec.env``. + """ + if self.spec.build_env_vars is not None: + return self.spec.build_env_vars(self.spec, config) + + if not self.spec.env: + return {} + + base_url = base_url_override or ( + config.base_url if hasattr(config, "base_url") else "" + ) + api_key = config.api_key if hasattr(config, "api_key") else "intercepted" + model = config.model if hasattr(config, "model") else "" + + substitutions = { + "base_url": base_url, + "api_key": api_key, + "model": model, + } + + resolved: dict[str, str] = {} + for key, value in self.spec.env.items(): + try: + resolved[key] = value.format(**substitutions) + except KeyError: + # If a placeholder isn't in our substitutions, keep it as-is. + resolved[key] = value + return resolved + + # Interception proxy + + def _start_proxy( + self, + sandbox: SandboxHandle, + *, + base_url: str, + api_key: str, + model: str, + ) -> tuple[BgJob, str, str]: + """Install deps, start proxy as bg job, wait for healthz. + + Returns ``(proxy_bg_job, base_url_override, proxy_trace_path)``. + """ + proxy_already_present = sandbox.exists("/home/user/proxy/interception.py") + + if not proxy_already_present: + self._exec_with_retry( + sandbox, + "pip install --quiet 'fastapi>=0.104' 'uvicorn[standard]>=0.24' " + "'httpx>=0.27' 2>&1 | tail -20", + timeout=180, + attempts=3, + backoff_s=2.0, + label="proxy deps install", + ) + sandbox.write_text( + "/home/user/proxy/interception.py", + _PROXY_SOURCE_PATH.read_text(), + ) + sandbox.write_text("/home/user/proxy/__init__.py", "") + + proxy_args = [ + "python", + "interception.py", + "--upstream-url", + base_url, + "--trace", + _PROXY_TRACE_PATH, + "--port", + str(_PROXY_PORT), + "--top-logprobs", + str(self._proxy_top_logprobs), + ] + if self._proxy_max_tokens_cap is not None: + proxy_args.extend(["--max-tokens-cap", str(self._proxy_max_tokens_cap)]) + if self._proxy_disable_thinking: + proxy_args.append("--disable-thinking") + if model: + proxy_args.extend(["--model-override", model]) + + quoted = " ".join(shlex.quote(a) for a in proxy_args) + proxy_cmd = ( + f"cd /home/user/proxy && {quoted} > {shlex.quote(_PROXY_LOG_PATH)} 2>&1" + ) + proxy_env = {"OPENCODE_UPSTREAM_API_KEY": api_key} + proxy_job = sandbox.start_bg(proxy_cmd, envs=proxy_env) + + # Wait for proxy healthz + attempts = 120 + interval_s = 0.5 + for _ in range(attempts): + r = sandbox.exec( + f"curl -sf http://127.0.0.1:{_PROXY_PORT}/healthz", + timeout=5, + ) + if r.exit_code == 0: + break + time.sleep(interval_s) + else: + log_content = "" + try: + log_content = sandbox.read_text(_PROXY_LOG_PATH) + except Exception: + pass + proxy_job.kill() + raise RuntimeError( + f"proxy did not start within {attempts * interval_s:.0f}s. " + f"log:\n{log_content[-2000:]}" + ) + + override_url = f"http://127.0.0.1:{_PROXY_PORT}/v1" + return proxy_job, override_url, _PROXY_TRACE_PATH + + # Utilities + + def _exec_with_retry( + self, + sandbox: SandboxHandle, + cmd: str, + *, + timeout: float, + attempts: int = 3, + backoff_s: float = 3.0, + label: str = "cmd", + ) -> Any: + """Run ``sandbox.exec`` with exponential backoff on transient failure.""" + last_stdout = "" + last_stderr = "" + last_exit = 0 + for i in range(attempts): + try: + r = sandbox.exec(cmd, timeout=timeout) + if r.exit_code == 0: + return r + last_stdout = r.stdout or "" + last_stderr = r.stderr or "" + last_exit = r.exit_code + if last_stderr.strip(): + break + except Exception as exc: + last_stderr = f"{type(exc).__name__}: {exc}" + last_exit = -1 + if i + 1 < attempts: + time.sleep(backoff_s * (2**i)) + raise RuntimeError( + f"{label} failed after {attempts} attempts " + f"(exit={last_exit}, stderr={last_stderr!r}, " + f"stdout_tail={last_stdout[-400:]!r})" + ) + + +# CLIAgentSessionFactory + + +class CLIAgentSessionFactory(ResourceSessionFactory): + """Factory that produces :class:`CLIAgentSession` instances for any + registered agent. + + Wraps :class:`CLIAgentDriver` to satisfy the + :class:`ResourceSessionFactory` contract from PR #603. + """ + + def __init__( + self, + *, + spec: CLIAgentSpec, + config: Any, + sandbox_backend: SandboxBackend, + mode: Literal["black_box", "transparent_proxy"] = "black_box", + verifier: Verifier | None = None, + install_timeout_s: int = 240, + setup_timeout_s: int = 300, + proxy_top_logprobs: int = 5, + proxy_max_tokens_cap: int | None = 16384, + proxy_disable_thinking: bool = False, + ) -> None: + self._spec = spec + self._config = config + self._verifier = verifier + self._driver = CLIAgentDriver( + spec=spec, + sandbox_backend=sandbox_backend, + mode=mode, + install_timeout_s=install_timeout_s, + setup_timeout_s=setup_timeout_s, + proxy_top_logprobs=proxy_top_logprobs, + proxy_max_tokens_cap=proxy_max_tokens_cap, + proxy_disable_thinking=proxy_disable_thinking, + ) + + def create( + self, + task: Any, + seed: int | None = None, + episode_id: str | None = None, + ) -> CLIAgentSession: + """Create one isolated session for a rollout.""" + return self._driver.create_session( + task=task, + config=self._config, + verifier=self._verifier, + seed=seed, + episode_id=episode_id, + ) + + +__all__ = [ + "CLIAgentDriver", + "CLIAgentSession", + "CLIAgentSessionFactory", + "Verifier", +] diff --git a/src/openenv/core/harness/agents/opencode.py b/src/openenv/core/harness/agents/opencode.py new file mode 100644 index 000000000..b179e9c9f --- /dev/null +++ b/src/openenv/core/harness/agents/opencode.py @@ -0,0 +1,191 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""OpenCode agent adapter. + +Expresses the OpenCode harness as a purely declarative :class:`CLIAgentSpec`. +All builders (command construction, config generation, env var resolution) +are self-contained with no imports from ``envs/opencode_env/``. + +Registered on import:: + + import openenv.core.harness.agents.opencode + # OPENCODE_SPEC is now in the registry +""" + +from __future__ import annotations + +import json +from typing import Any + +from . import register_agent +from .base import AgentEvent, ArtifactSpec, CLIAgentSpec, MCPConfigSpec + + +# Command / config / env builders + + +def _build_opencode_command( + spec: CLIAgentSpec, + config: Any, + task: Any, + mcp_config_path: str | None, +) -> str: + """Build the ``opencode run`` shell command.""" + home = config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" + run_format = config.run_format if hasattr(config, "run_format") else "json" + format_flag = "--format json" if run_format == "json" else "" + instruction_file = f"{home}/task/instruction.md" + log_file = f"{home}/logs/agent/opencode.jsonl" + workdir = f"{home}/workdir" + + return ( + f'export PATH="$HOME/.opencode/bin:$PATH" && ' + f"cd {workdir} && " + f'opencode run {format_flag} "$(cat {instruction_file})" ' + f"2>&1 | tee {log_file}" + ).strip() + + +def _build_opencode_mcp_config( + spec: CLIAgentSpec, + tools: list[Any], + workdir: str, +) -> str: + """Build the ``opencode.json`` content for the MCP config file.""" + return json.dumps( + { + "$schema": "https://opencode.ai/config.json", + "model": "intercepted/model", + "provider": { + "intercepted": { + "npm": "@ai-sdk/openai-compatible", + "name": "Intercepted", + "options": { + "baseURL": "http://127.0.0.1:7000/v1", + "apiKey": "intercepted", + "timeout": 600000, + }, + "models": { + "model": {"name": "Intercepted Model"}, + }, + } + }, + }, + indent=2, + ) + + +def _build_opencode_env_vars( + spec: CLIAgentSpec, + config: Any, +) -> dict[str, str]: + """Build env vars for the OpenCode process.""" + home = config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" + base_url = config.base_url if hasattr(config, "base_url") else "" + api_key = config.api_key if hasattr(config, "api_key") else "intercepted" + extra_env = config.extra_env if hasattr(config, "extra_env") else {} + + env = dict(extra_env) + env["OPENAI_BASE_URL"] = base_url + env["OPENAI_API_KEY"] = api_key + env["OPENCODE_CONFIG"] = f"{home}/.config/opencode/opencode.json" + return env + + +def _parse_opencode_event(line: str) -> AgentEvent | None: + """Parse one line of OpenCode's JSONL stdout.""" + line = line.strip() + if not line: + return None + try: + data = json.loads(line) + except json.JSONDecodeError: + return None + + event_type = data.get("type", "") + if event_type in ("assistant", "message"): + return AgentEvent(type="assistant", data=data, raw=line) + elif event_type in ("tool_call", "tool_use"): + return AgentEvent(type="tool_call", data=data, raw=line) + elif event_type in ("tool_result", "tool_response"): + return AgentEvent(type="tool_result", data=data, raw=line) + elif event_type == "error": + return AgentEvent(type="error", data=data, raw=line) + elif event_type in ("done", "complete", "end"): + return AgentEvent(type="done", data=data, raw=line) + return AgentEvent(type="assistant", data=data, raw=line) + + +# File resolvers + + +def _instruction_file_content(task: Any, config: Any) -> str: + return task.instruction if hasattr(task, "instruction") else str(task) + + +def _system_prompt_content(task: Any, config: Any) -> str | None: + if hasattr(config, "system_prompt") and config.system_prompt: + return config.system_prompt + return None + + +# Spec definition + + +OPENCODE_SPEC = CLIAgentSpec( + name="opencode", + install_check_cmd=["/home/user/.opencode/bin/opencode", "--version"], + base_command=[ + "opencode", + "run", + "--format", + "json", + "--dangerously-skip-permissions", + ], + mcp_config=MCPConfigSpec( + method="config_file", + path_template="{home}/.config/opencode/opencode.json", + ), + supports_logprob_proxy=True, + default_timeout_s=900.0, + setup=( + "set -e && " + "mkdir -p /home/user/.config/opencode /home/user/logs/agent " + "/home/user/logs/verifier /home/user/task /home/user/workdir && " + "curl -fsSL https://opencode.ai/install | bash && " + 'export PATH="$HOME/.opencode/bin:$PATH" && ' + "opencode --version" + ), + files={ + "/home/user/task/instruction.md": _instruction_file_content, + "/home/user/task/system.md": _system_prompt_content, + }, + artifacts={ + "agent_log": ArtifactSpec( + path="/home/user/logs/agent/opencode.jsonl", + format="jsonl", + ), + }, + env={ + "PATH": "/home/user/.opencode/bin:$PATH", + "OPENAI_BASE_URL": "{base_url}", + "OPENAI_API_KEY": "{api_key}", + }, + build_command=_build_opencode_command, + build_mcp_config=_build_opencode_mcp_config, + parse_events=_parse_opencode_event, + build_env_vars=_build_opencode_env_vars, +) + + +# Auto-register on import +register_agent(OPENCODE_SPEC) + + +__all__ = [ + "OPENCODE_SPEC", +] From 455b0e9e46b266655ec05558a321814593ee5cfd Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Tue, 12 May 2026 23:02:10 +0530 Subject: [PATCH 03/79] feat: add tests --- tests/core/test_cli_agent_driver.py | 1064 +++++++++++++++++++++++++++ tests/envs/test_opencode_env.py | 8 +- 2 files changed, 1067 insertions(+), 5 deletions(-) create mode 100644 tests/core/test_cli_agent_driver.py diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py new file mode 100644 index 000000000..b26f01d67 --- /dev/null +++ b/tests/core/test_cli_agent_driver.py @@ -0,0 +1,1064 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Tests for the CLI agent driver abstraction (Phase 2). + +Covers: + - Agent spec + event protocols (base.py) + - Agent registry (__init__.py) + - CLIAgentDriver / CLIAgentSession / CLIAgentSessionFactory (cli_driver.py) + - OpenCode adapter spec (opencode.py) + +All tests run without external dependencies (no E2B, no LLM, no network). +""" + +from __future__ import annotations + +import json +from dataclasses import dataclass, field +from typing import Any + +import pytest + + +# Fake sandbox infrastructure (mirrors test_opencode_env.py pattern) + + +@dataclass +class FakeExecResult: + exit_code: int = 0 + stdout: str = "ok" + stderr: str = "" + + +@dataclass +class FakeBgJob: + cmd: str = "" + envs: dict[str, str] | None = None + _exit_code: int = 0 + + @property + def pid(self) -> int: + return 12345 + + def wait(self, timeout: float | None = None) -> int: + return self._exit_code + + def kill(self) -> None: + pass + + +class FakeSandbox: + """In-memory sandbox for unit testing.""" + + def __init__( + self, + *, + install_check_succeeds: bool = False, + healthz_succeeds: bool = True, + ) -> None: + self.sandbox_id = "fake-sandbox-001" + self.written: dict[str, str] = {} + self.executed: list[str] = [] + self.bg_commands: list[tuple[str, dict[str, str] | None]] = [] + self._install_check_succeeds = install_check_succeeds + self._healthz_succeeds = healthz_succeeds + self._killed = False + + def exec( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + timeout: float | None = 60, + ) -> FakeExecResult: + self.executed.append(cmd) + if cmd == "echo ok": + return FakeExecResult(exit_code=0, stdout="ok") + # install check — only standalone version-check commands (short, just + # binary + --version) should be treated as install probes. Multi-part + # setup scripts that happen to end with --version should succeed. + if "--version" in cmd and len(cmd) < 80 and "&&" not in cmd: + if self._install_check_succeeds: + return FakeExecResult(exit_code=0, stdout="1.0.0") + return FakeExecResult(exit_code=127, stderr="not found") + # healthz check + if "healthz" in cmd: + if self._healthz_succeeds: + return FakeExecResult(exit_code=0, stdout='{"status":"ok"}') + return FakeExecResult(exit_code=7, stderr="connection refused") + # All other commands succeed + return FakeExecResult(exit_code=0, stdout="") + + def start_bg( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + ) -> FakeBgJob: + self.bg_commands.append((cmd, envs)) + return FakeBgJob(cmd=cmd, envs=envs) + + def write_text(self, path: str, content: str) -> None: + self.written[path] = content + + def read_text(self, path: str) -> str: + if path not in self.written: + raise FileNotFoundError(f"No such file: {path}") + return self.written[path] + + def exists(self, path: str) -> bool: + return path in self.written + + def kill(self) -> None: + self._killed = True + + +class FakeSandboxBackend: + """Backend that returns FakeSandbox instances.""" + + def __init__( + self, + *, + install_check_succeeds: bool = False, + healthz_succeeds: bool = True, + ) -> None: + self._install_check_succeeds = install_check_succeeds + self._healthz_succeeds = healthz_succeeds + self.created: list[FakeSandbox] = [] + + def create( + self, + *, + timeout_s: int = 900, + envs: dict[str, str] | None = None, + metadata: dict[str, str] | None = None, + ) -> FakeSandbox: + sbx = FakeSandbox( + install_check_succeeds=self._install_check_succeeds, + healthz_succeeds=self._healthz_succeeds, + ) + self.created.append(sbx) + return sbx + + +@dataclass +class FakeTask: + instruction: str = "Write hello.py" + setup_shell: str | None = None + upload_files: dict[str, str] = field(default_factory=dict) + metadata: dict[str, Any] = field(default_factory=dict) + + +@dataclass +class FakeConfig: + base_url: str = "https://api.example.com/v1" + api_key: str = "sk-test-key" + model: str = "test-model" + agent_timeout_s: float = 300.0 + sandbox_home: str = "/home/user" + extra_env: dict[str, str] = field(default_factory=dict) + + +# PR 2.1: Agent Spec and Event Parser Protocols + + +class TestAgentSpecProtocols: + """Tests for base.py data models.""" + + def test_mcp_config_spec_frozen(self): + from openenv.core.harness.agents.base import MCPConfigSpec + + spec = MCPConfigSpec(method="config_file", path_template="{workdir}/mcp.json") + assert spec.method == "config_file" + assert spec.path_template == "{workdir}/mcp.json" + with pytest.raises(AttributeError): + spec.method = "cli_flags" # type: ignore[misc] + + def test_artifact_spec_defaults(self): + from openenv.core.harness.agents.base import ArtifactSpec + + a = ArtifactSpec(path="/logs/agent/out.log") + assert a.format == "text" + assert a.optional is True + + def test_artifact_spec_json(self): + from openenv.core.harness.agents.base import ArtifactSpec + + a = ArtifactSpec(path="/data/traj.json", format="json", optional=False) + assert a.format == "json" + assert a.optional is False + + def test_agent_event_creation(self): + from openenv.core.harness.agents.base import AgentEvent + + e = AgentEvent( + type="tool_call", data={"name": "bash"}, raw='{"type":"tool_call"}' + ) + assert e.type == "tool_call" + assert e.data["name"] == "bash" + + def test_cli_agent_spec_minimal(self): + from openenv.core.harness.agents.base import CLIAgentSpec, MCPConfigSpec + + spec = CLIAgentSpec( + name="test-agent", + install_check_cmd=["test-agent", "--version"], + base_command=["test-agent", "run"], + mcp_config=MCPConfigSpec(method="cli_flags"), + ) + assert spec.name == "test-agent" + assert spec.supports_logprob_proxy is True + assert spec.default_timeout_s == 600.0 + assert spec.setup is None + assert spec.files is None + assert spec.artifacts is None + assert spec.env is None + assert spec.build_command is None + + def test_cli_agent_spec_full(self): + from openenv.core.harness.agents.base import ( + ArtifactSpec, + CLIAgentSpec, + MCPConfigSpec, + ) + + spec = CLIAgentSpec( + name="full-agent", + install_check_cmd=["full-agent", "--version"], + base_command=["full-agent", "exec"], + mcp_config=MCPConfigSpec( + method="config_file", path_template="{workdir}/mcp.json" + ), + supports_logprob_proxy=True, + default_timeout_s=900.0, + setup="npm install -g full-agent", + files={ + "/task.txt": "hello", + "/dynamic.txt": lambda task, config: task.instruction, + }, + artifacts={ + "log": ArtifactSpec(path="/logs/out.log"), + "traj": ArtifactSpec(path="/logs/traj.json", format="json"), + }, + env={"API_KEY": "{api_key}", "MODEL": "{model}"}, + build_command=lambda spec, config, task, mcp: "full-agent exec", + build_mcp_config=lambda spec, tools, workdir: "{}", + parse_events=lambda line: None, + ) + assert spec.name == "full-agent" + assert len(spec.artifacts) == 2 + assert callable(spec.files["/dynamic.txt"]) + + +# PR 2.2: Agent Registry + + +class TestAgentRegistry: + """Tests for the agent registry.""" + + def test_register_and_lookup(self): + from openenv.core.harness.agents import ( + get_agent_spec, + list_agents, + register_agent, + unregister_agent, + ) + from openenv.core.harness.agents.base import CLIAgentSpec, MCPConfigSpec + + spec = CLIAgentSpec( + name="test-registry-agent", + install_check_cmd=["tra", "--version"], + base_command=["tra", "run"], + mcp_config=MCPConfigSpec(method="cli_flags"), + ) + try: + register_agent(spec) + assert "test-registry-agent" in list_agents() + assert get_agent_spec("test-registry-agent") is spec + finally: + unregister_agent("test-registry-agent") + + def test_duplicate_registration_same_object_ok(self): + from openenv.core.harness.agents import register_agent, unregister_agent + from openenv.core.harness.agents.base import CLIAgentSpec, MCPConfigSpec + + spec = CLIAgentSpec( + name="test-dup-ok", + install_check_cmd=["x"], + base_command=["x"], + mcp_config=MCPConfigSpec(method="cli_flags"), + ) + try: + register_agent(spec) + register_agent(spec) # same object — should be fine + finally: + unregister_agent("test-dup-ok") + + def test_duplicate_registration_different_object_raises(self): + from openenv.core.harness.agents import register_agent, unregister_agent + from openenv.core.harness.agents.base import CLIAgentSpec, MCPConfigSpec + + spec1 = CLIAgentSpec( + name="test-dup-fail", + install_check_cmd=["x"], + base_command=["x"], + mcp_config=MCPConfigSpec(method="cli_flags"), + ) + spec2 = CLIAgentSpec( + name="test-dup-fail", + install_check_cmd=["y"], + base_command=["y"], + mcp_config=MCPConfigSpec(method="cli_flags"), + ) + try: + register_agent(spec1) + with pytest.raises(ValueError, match="already registered"): + register_agent(spec2) + finally: + unregister_agent("test-dup-fail") + + def test_unknown_agent_raises_keyerror(self): + from openenv.core.harness.agents import get_agent_spec + + with pytest.raises(KeyError, match="Unknown agent"): + get_agent_spec("nonexistent-agent-xyz") + + def test_unregister_returns_spec(self): + from openenv.core.harness.agents import register_agent, unregister_agent + from openenv.core.harness.agents.base import CLIAgentSpec, MCPConfigSpec + + spec = CLIAgentSpec( + name="test-unreg", + install_check_cmd=["x"], + base_command=["x"], + mcp_config=MCPConfigSpec(method="cli_flags"), + ) + register_agent(spec) + removed = unregister_agent("test-unreg") + assert removed is spec + assert unregister_agent("test-unreg") is None + + def test_auto_import_opencode(self): + """Auto-import triggers registration of built-in agents.""" + from openenv.core.harness.agents import get_agent_spec + + spec = get_agent_spec("opencode") + assert spec.name == "opencode" + + +# PR 2.3: CLIAgentDriver / CLIAgentSession / CLIAgentSessionFactory + + +def _make_test_spec(**overrides: Any) -> Any: + from openenv.core.harness.agents.base import ( + ArtifactSpec, + CLIAgentSpec, + MCPConfigSpec, + ) + + defaults = dict( + name="test-agent", + install_check_cmd=["test-agent", "--version"], + base_command=["test-agent", "run", "--json"], + mcp_config=MCPConfigSpec( + method="config_file", path_template="{workdir}/mcp.json" + ), + setup="apt-get install -y test-agent", + files={ + "/home/user/task/instruction.txt": lambda task, config: task.instruction, + }, + artifacts={ + "agent_log": ArtifactSpec(path="/home/user/logs/agent.log"), + }, + env={ + "API_KEY": "{api_key}", + "BASE_URL": "{base_url}", + "MODEL": "{model}", + }, + build_command=lambda spec, config, task, mcp: ( + f"test-agent run --json '{task.instruction}' 2>&1 | tee /home/user/logs/agent.log" + ), + build_mcp_config=lambda spec, tools, workdir: json.dumps({"tools": []}), + parse_events=lambda line: None, + ) + defaults.update(overrides) + return CLIAgentSpec(**defaults) + + +class TestCLIAgentDriver: + """Tests for the shared CLI agent driver.""" + + def test_create_session_full_lifecycle(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + task = FakeTask(instruction="Write hello.py") + config = FakeConfig() + session = driver.create_session(task=task, config=config) + + # Verify sandbox was created + assert len(backend.created) == 1 + sbx = backend.created[0] + + # Verify sandbox readiness was probed + assert "echo ok" in sbx.executed + + # Verify install was attempted (agent not pre-installed) + assert any("apt-get install" in cmd for cmd in sbx.executed) + + # Verify files were uploaded + assert "/home/user/task/instruction.txt" in sbx.written + assert sbx.written["/home/user/task/instruction.txt"] == "Write hello.py" + + # Verify MCP config was written + assert "/home/user/workdir/mcp.json" in sbx.written + + # Verify agent was launched as bg process + assert len(sbx.bg_commands) == 1 + bg_cmd, bg_envs = sbx.bg_commands[0] + assert "test-agent run" in bg_cmd + + # Verify env vars were resolved + assert bg_envs["API_KEY"] == "sk-test-key" + assert bg_envs["BASE_URL"] == "https://api.example.com/v1" + assert bg_envs["MODEL"] == "test-model" + + # Session API + assert session.initial_messages() == [ + {"role": "user", "content": "Write hello.py"} + ] + assert session.list_tools() == [] + assert session.call_tool("x", {}).error is not None + assert session.wait_for_completion() == 0 + + session.close() + assert sbx._killed + + def test_create_session_skips_install_when_prebaked(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend(install_check_succeeds=True) + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + session = driver.create_session( + task=FakeTask(), + config=FakeConfig(), + ) + + sbx = backend.created[0] + # install should have been skipped + assert not any("apt-get install" in cmd for cmd in sbx.executed) + session.close() + + def test_create_session_with_proxy(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend() + driver = CLIAgentDriver( + spec=spec, + sandbox_backend=backend, + mode="transparent_proxy", + ) + + session = driver.create_session( + task=FakeTask(), + config=FakeConfig(), + ) + + sbx = backend.created[0] + + # Proxy source should have been uploaded + assert "/home/user/proxy/interception.py" in sbx.written + assert "/home/user/proxy/__init__.py" in sbx.written + + # Proxy should have been started as bg (before agent) + # and agent as second bg + assert len(sbx.bg_commands) == 2 + proxy_cmd, proxy_envs = sbx.bg_commands[0] + assert "interception.py" in proxy_cmd + assert proxy_envs == {"OPENCODE_UPSTREAM_API_KEY": "sk-test-key"} + + # Agent env should point at proxy + agent_cmd, agent_envs = sbx.bg_commands[1] + assert agent_envs["BASE_URL"] == "http://127.0.0.1:7000/v1" + + session.close() + + def test_create_session_uploads_task_files(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + task = FakeTask( + instruction="Write code", + upload_files={"/extra/data.json": '{"key": "value"}'}, + ) + session = driver.create_session(task=task, config=FakeConfig()) + + sbx = backend.created[0] + assert sbx.written["/extra/data.json"] == '{"key": "value"}' + session.close() + + def test_create_session_runs_task_setup_shell(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + task = FakeTask( + instruction="Write code", + setup_shell="pip install pandas", + ) + session = driver.create_session(task=task, config=FakeConfig()) + + sbx = backend.created[0] + assert "pip install pandas" in sbx.executed + session.close() + + def test_create_session_with_verifier(self): + from openenv.core.harness import VerifyResult + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + def verifier(sandbox, task): + return VerifyResult(env_reward=1.0, done=True, metrics={"correct": True}) + + session = driver.create_session( + task=FakeTask(), + config=FakeConfig(), + verifier=verifier, + ) + + result = session.verify([]) + assert result.env_reward == 1.0 + assert result.metrics["correct"] is True + session.close() + + def test_session_verify_without_verifier(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + session = driver.create_session(task=FakeTask(), config=FakeConfig()) + + result = session.verify([]) + assert result.env_reward is None + assert result.done is True + session.close() + + def test_invalid_mode_raises(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + with pytest.raises(ValueError, match="Unknown mode"): + CLIAgentDriver( + spec=spec, + sandbox_backend=FakeSandboxBackend(), + mode="invalid", # type: ignore[arg-type] + ) + + +class TestCLIAgentSession: + """Tests for CLIAgentSession.""" + + def test_collect_artifacts_text(self): + from openenv.core.harness.agents.base import ArtifactSpec + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec( + artifacts={ + "log": ArtifactSpec(path="/logs/out.log"), + }, + ) + sbx = FakeSandbox() + sbx.written["/logs/out.log"] = "line1\nline2\n" + + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + ) + arts = session.collect_artifacts() + assert arts["log"] == "line1\nline2\n" + + def test_collect_artifacts_json(self): + from openenv.core.harness.agents.base import ArtifactSpec + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec( + artifacts={ + "traj": ArtifactSpec(path="/logs/traj.json", format="json"), + }, + ) + sbx = FakeSandbox() + sbx.written["/logs/traj.json"] = json.dumps({"steps": [1, 2, 3]}) + + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + ) + arts = session.collect_artifacts() + assert arts["traj"] == {"steps": [1, 2, 3]} + + def test_collect_artifacts_jsonl(self): + from openenv.core.harness.agents.base import ArtifactSpec + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec( + artifacts={ + "events": ArtifactSpec(path="/logs/events.jsonl", format="jsonl"), + }, + ) + sbx = FakeSandbox() + sbx.written["/logs/events.jsonl"] = '{"a":1}\n{"b":2}\n' + + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + ) + arts = session.collect_artifacts() + assert arts["events"] == [{"a": 1}, {"b": 2}] + + def test_collect_artifacts_missing_optional(self): + from openenv.core.harness.agents.base import ArtifactSpec + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec( + artifacts={ + "log": ArtifactSpec(path="/missing/file.log", optional=True), + }, + ) + sbx = FakeSandbox() + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + ) + arts = session.collect_artifacts() + assert "log" not in arts + + def test_collect_artifacts_missing_required_raises(self): + from openenv.core.harness.agents.base import ArtifactSpec + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec( + artifacts={ + "log": ArtifactSpec(path="/missing/file.log", optional=False), + }, + ) + sbx = FakeSandbox() + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + ) + with pytest.raises(FileNotFoundError): + session.collect_artifacts() + + def test_fetch_proxy_trace_black_box(self): + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec() + session = CLIAgentSession( + spec=spec, + sandbox=FakeSandbox(), + task=FakeTask(), + config=FakeConfig(), + proxy_trace_path=None, + ) + assert session.fetch_proxy_trace() == [] + + def test_fetch_proxy_trace_with_data(self): + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec() + sbx = FakeSandbox() + trace_path = "/logs/proxy_trace.jsonl" + sbx.written[trace_path] = ( + json.dumps({"turn": 1, "latency_s": 0.5}) + + "\n" + + json.dumps({"turn": 2, "latency_s": 0.3}) + + "\n" + ) + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + proxy_trace_path=trace_path, + ) + trace = session.fetch_proxy_trace() + assert len(trace) == 2 + assert trace[0]["turn"] == 1 + + def test_close_kills_sandbox_and_jobs(self): + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + spec = _make_test_spec() + sbx = FakeSandbox() + agent_job = FakeBgJob() + proxy_job = FakeBgJob() + + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + agent_bg_job=agent_job, + proxy_bg_job=proxy_job, + ) + session.close() + assert sbx._killed + assert session._agent_bg_job is None + assert session._proxy_bg_job is None + + +class TestCLIAgentSessionFactory: + """Tests for the ResourceSessionFactory wrapper.""" + + def test_factory_creates_sessions(self): + from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory + + spec = _make_test_spec() + backend = FakeSandboxBackend() + + factory = CLIAgentSessionFactory( + spec=spec, + config=FakeConfig(), + sandbox_backend=backend, + mode="black_box", + ) + + session = factory.create(task=FakeTask()) + assert len(backend.created) == 1 + assert session.initial_messages()[0]["content"] == "Write hello.py" + session.close() + + def test_factory_with_verifier(self): + from openenv.core.harness import VerifyResult + from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory + + spec = _make_test_spec() + backend = FakeSandboxBackend() + + def verifier(sandbox, task): + return VerifyResult(env_reward=0.5, done=True) + + factory = CLIAgentSessionFactory( + spec=spec, + config=FakeConfig(), + sandbox_backend=backend, + mode="black_box", + verifier=verifier, + ) + + session = factory.create(task=FakeTask()) + result = session.verify([]) + assert result.env_reward == 0.5 + session.close() + + def test_factory_implements_resource_session_factory(self): + from openenv.core.harness import ResourceSessionFactory + from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory + + assert issubclass(CLIAgentSessionFactory, ResourceSessionFactory) + + def test_session_implements_resource_session(self): + from openenv.core.harness import ResourceSession + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + assert issubclass(CLIAgentSession, ResourceSession) + + +# PR 2.4: OpenCode Adapter Spec + + +class TestOpenCodeSpec: + """Tests for the OpenCode declarative spec.""" + + def test_spec_is_registered(self): + from openenv.core.harness.agents import get_agent_spec + + spec = get_agent_spec("opencode") + assert spec.name == "opencode" + + def test_spec_fields(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + assert OPENCODE_SPEC.name == "opencode" + assert OPENCODE_SPEC.install_check_cmd == [ + "/home/user/.opencode/bin/opencode", + "--version", + ] + assert OPENCODE_SPEC.supports_logprob_proxy is True + assert OPENCODE_SPEC.default_timeout_s == 900.0 + assert OPENCODE_SPEC.mcp_config.method == "config_file" + assert "{home}" in OPENCODE_SPEC.mcp_config.path_template + assert OPENCODE_SPEC.artifacts is not None + assert "agent_log" in OPENCODE_SPEC.artifacts + assert OPENCODE_SPEC.artifacts["agent_log"].format == "jsonl" + + def test_build_command(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + @dataclass + class OcConfig: + sandbox_home: str = "/home/user" + run_format: str = "json" + + cmd = OPENCODE_SPEC.build_command( + OPENCODE_SPEC, + OcConfig(), + FakeTask(instruction="Write hello.py"), + None, + ) + assert "opencode run" in cmd + assert "--format json" in cmd + assert "/home/user/task/instruction.md" in cmd + + def test_build_mcp_config(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + config_str = OPENCODE_SPEC.build_mcp_config( + OPENCODE_SPEC, + [], + "/home/user/workdir", + ) + config = json.loads(config_str) + assert "$schema" in config + assert "provider" in config + + def test_parse_events_assistant(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + line = json.dumps({"type": "assistant", "content": "hello"}) + event = OPENCODE_SPEC.parse_events(line) + assert event is not None + assert event.type == "assistant" + + def test_parse_events_tool_call(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + line = json.dumps({"type": "tool_call", "name": "bash", "args": {}}) + event = OPENCODE_SPEC.parse_events(line) + assert event is not None + assert event.type == "tool_call" + + def test_parse_events_error(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + line = json.dumps({"type": "error", "message": "boom"}) + event = OPENCODE_SPEC.parse_events(line) + assert event is not None + assert event.type == "error" + + def test_parse_events_done(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + line = json.dumps({"type": "done"}) + event = OPENCODE_SPEC.parse_events(line) + assert event is not None + assert event.type == "done" + + def test_parse_events_invalid_json(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + assert OPENCODE_SPEC.parse_events("not json") is None + assert OPENCODE_SPEC.parse_events("") is None + + def test_build_env_vars(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + config = FakeConfig() + config.extra_env = {"EXTRA": "val"} + envs = OPENCODE_SPEC.build_env_vars(OPENCODE_SPEC, config) + assert envs["OPENAI_BASE_URL"] == "https://api.example.com/v1" + assert envs["OPENAI_API_KEY"] == "sk-test-key" + assert envs["OPENCODE_CONFIG"] == "/home/user/.config/opencode/opencode.json" + assert envs["EXTRA"] == "val" + + def test_files_instruction_resolver(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + task = FakeTask(instruction="Build a REST API") + config = FakeConfig() + instruction_fn = OPENCODE_SPEC.files["/home/user/task/instruction.md"] + assert callable(instruction_fn) + assert instruction_fn(task, config) == "Build a REST API" + + def test_files_system_prompt_resolver(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + task = FakeTask() + config = FakeConfig() + system_fn = OPENCODE_SPEC.files["/home/user/task/system.md"] + assert callable(system_fn) + # No system prompt on FakeConfig → returns None + assert system_fn(task, config) is None + + def test_opencode_driver_integration(self): + """End-to-end: create a session using the OpenCode spec via the driver.""" + from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + backend = FakeSandboxBackend() + factory = CLIAgentSessionFactory( + spec=OPENCODE_SPEC, + config=FakeConfig(), + sandbox_backend=backend, + mode="black_box", + ) + + session = factory.create(task=FakeTask(instruction="Hello")) + assert session.spec.name == "opencode" + assert session.initial_messages()[0]["content"] == "Hello" + + sbx = backend.created[0] + # Instruction file should have been written + assert sbx.written.get("/home/user/task/instruction.md") == "Hello" + + session.close() + + +# Env var resolution + + +class TestEnvVarResolution: + """Tests for environment variable placeholder resolution.""" + + def test_resolve_placeholders(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec( + env={ + "KEY": "{api_key}", + "URL": "{base_url}", + "MDL": "{model}", + "STATIC": "fixed_value", + }, + build_env_vars=None, # use placeholder resolution + ) + driver = CLIAgentDriver( + spec=spec, + sandbox_backend=FakeSandboxBackend(), + mode="black_box", + ) + envs = driver._resolve_env_vars(FakeConfig()) + assert envs["KEY"] == "sk-test-key" + assert envs["URL"] == "https://api.example.com/v1" + assert envs["MDL"] == "test-model" + assert envs["STATIC"] == "fixed_value" + + def test_resolve_with_proxy_override(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec( + env={"URL": "{base_url}"}, + build_env_vars=None, + ) + driver = CLIAgentDriver( + spec=spec, + sandbox_backend=FakeSandboxBackend(), + mode="black_box", + ) + envs = driver._resolve_env_vars( + FakeConfig(), + base_url_override="http://127.0.0.1:7000/v1", + ) + assert envs["URL"] == "http://127.0.0.1:7000/v1" + + def test_build_env_vars_hook_takes_precedence(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + def custom_env(spec, config): + return {"CUSTOM": "yes", "MODEL": config.model} + + spec = _make_test_spec( + env={"SHOULD_NOT": "appear"}, + build_env_vars=custom_env, + ) + driver = CLIAgentDriver( + spec=spec, + sandbox_backend=FakeSandboxBackend(), + mode="black_box", + ) + envs = driver._resolve_env_vars(FakeConfig()) + assert envs == {"CUSTOM": "yes", "MODEL": "test-model"} + assert "SHOULD_NOT" not in envs + + def test_empty_env_dict(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec(env=None, build_env_vars=None) + driver = CLIAgentDriver( + spec=spec, + sandbox_backend=FakeSandboxBackend(), + mode="black_box", + ) + envs = driver._resolve_env_vars(FakeConfig()) + assert envs == {} + + +# Multiple setup commands + + +class TestMultiStepSetup: + """Tests for specs with multi-step setup commands.""" + + def test_list_of_setup_commands(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec( + setup=[ + "apt-get update", + "apt-get install -y nodejs", + "npm install -g test-agent", + ], + ) + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + session = driver.create_session(task=FakeTask(), config=FakeConfig()) + sbx = backend.created[0] + + # All three setup commands should have been executed + assert any("apt-get update" in cmd for cmd in sbx.executed) + assert any("apt-get install" in cmd for cmd in sbx.executed) + assert any("npm install" in cmd for cmd in sbx.executed) + session.close() + + def test_no_setup_and_not_installed_raises(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec(setup=None) + backend = FakeSandboxBackend(install_check_succeeds=False) + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + with pytest.raises(RuntimeError, match="not installed"): + driver.create_session(task=FakeTask(), config=FakeConfig()) diff --git a/tests/envs/test_opencode_env.py b/tests/envs/test_opencode_env.py index 6014c9199..5e930b8bc 100644 --- a/tests/envs/test_opencode_env.py +++ b/tests/envs/test_opencode_env.py @@ -276,10 +276,6 @@ def exists(self, path: str) -> bool: def kill(self) -> None: pass - class NoopInstallFactory(OpenCodeSessionFactory): - def _exec_with_retry(self, *args, **kwargs): - return FakeExecResult() - secret = "sk-test '$(leak)" model = "provider/model'; touch /tmp/pwn #" config = OpenCodeConfig( @@ -288,12 +284,14 @@ def _exec_with_retry(self, *args, **kwargs): model=model, ) sandbox = FakeSandbox() - factory = NoopInstallFactory( + factory = OpenCodeSessionFactory( config=config, sandbox_backend=object(), # unused by this protected-method test mode="transparent_proxy", ) + # _start_proxy delegates to CLIAgentDriver._start_proxy which runs the + # proxy inside the sandbox. The driver handles dep install + source upload. factory._start_proxy(sandbox) assert sandbox.started_cmd is not None From e97fda0e0144cc566b17f262dc5c304749567b11 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 13 May 2026 11:22:09 +0530 Subject: [PATCH 04/79] feat: impl Docker sandbox backend - related tests --- src/openenv/core/harness/sandbox/__init__.py | 37 +- src/openenv/core/harness/sandbox/base.py | 2 +- .../core/harness/sandbox/docker_backend.py | 328 +++++++++++++++++ .../core/harness/sandbox/e2b_backend.py | 7 +- .../core/harness/sandbox/interception.py | 19 +- tests/core/test_docker_sandbox_backend.py | 335 ++++++++++++++++++ 6 files changed, 707 insertions(+), 21 deletions(-) create mode 100644 src/openenv/core/harness/sandbox/docker_backend.py create mode 100644 tests/core/test_docker_sandbox_backend.py diff --git a/src/openenv/core/harness/sandbox/__init__.py b/src/openenv/core/harness/sandbox/__init__.py index d0324a7d7..83d37fb48 100644 --- a/src/openenv/core/harness/sandbox/__init__.py +++ b/src/openenv/core/harness/sandbox/__init__.py @@ -7,25 +7,52 @@ """Sandbox backends for harness-driven rollouts. Provides the :class:`SandboxBackend` / :class:`SandboxHandle` protocols and -concrete implementations. Any harness adapter can use any backend — the +concrete implementations. Any harness adapter can use any backend -- the sandbox layer is orthogonal to the agent CLI choice. -The ``e2b`` import is wrapped in ``try/except`` so this package loads cleanly -in environments where ``e2b`` isn't installed (CI smoke tests, lint runs). +Optional backend imports are wrapped in ``try/except`` so this package +loads cleanly when dependencies aren't installed (CI smoke tests, lint). """ +from typing import Any, Literal + from .base import BgJob, ExecResult, SandboxBackend, SandboxHandle +from .docker_backend import DockerBgJob, DockerSandboxBackend, DockerSandboxHandle __all__ = [ "BgJob", + "DockerBgJob", + "DockerSandboxBackend", + "DockerSandboxHandle", "ExecResult", "SandboxBackend", "SandboxHandle", + "create_sandbox_backend", ] try: - from .e2b_backend import E2BBgJob, E2BSandboxBackend, E2BSandboxHandle + from .e2b_backend import E2BBgJob, E2BSandboxBackend, E2BSandboxHandle # noqa: F401 __all__.extend(["E2BBgJob", "E2BSandboxBackend", "E2BSandboxHandle"]) except ImportError: - pass # e2b not installed — stubs live in envs/opencode_env/sandbox/__init__.py + pass # e2b not installed + + +def create_sandbox_backend( + backend: Literal["e2b", "docker"] = "e2b", + **kwargs: Any, +) -> SandboxBackend: + """Create a sandbox backend by name. + + For ``"e2b"``: works with both E2B cloud and CubeSandbox + (set ``E2B_API_URL``). + + For ``"docker"``: local Docker, no external dependencies. + """ + if backend == "e2b": + from .e2b_backend import E2BSandboxBackend + + return E2BSandboxBackend(**kwargs) + elif backend == "docker": + return DockerSandboxBackend(**kwargs) + raise ValueError(f"Unknown sandbox backend: {backend!r}. Use 'e2b' or 'docker'.") diff --git a/src/openenv/core/harness/sandbox/base.py b/src/openenv/core/harness/sandbox/base.py index 4b2620799..d84e267e1 100644 --- a/src/openenv/core/harness/sandbox/base.py +++ b/src/openenv/core/harness/sandbox/base.py @@ -17,7 +17,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, Protocol, runtime_checkable +from typing import Protocol, runtime_checkable @dataclass diff --git a/src/openenv/core/harness/sandbox/docker_backend.py b/src/openenv/core/harness/sandbox/docker_backend.py new file mode 100644 index 000000000..aeaacad7c --- /dev/null +++ b/src/openenv/core/harness/sandbox/docker_backend.py @@ -0,0 +1,328 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Docker implementation of :class:`SandboxBackend`. + +Runs each sandbox as a ``docker run -d`` container on the local machine. +Commands execute via ``docker exec``, files transfer via ``docker exec`` +with stdin piping. Suitable for CI, local dev, and environments without +KVM or cloud sandbox credentials. + +Usage:: + + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create() + result = sandbox.exec("echo hello") + print(result.stdout) # "hello" + sandbox.kill() +""" + +from __future__ import annotations + +import logging +import subprocess +import threading +import time +import uuid +from pathlib import PurePosixPath + +from openenv.core.harness.sandbox.base import BgJob, ExecResult + +_log = logging.getLogger(__name__) + + +class DockerBgJob: + """Handle to a background process running inside a Docker container. + + Launches the command via ``docker exec -d`` and tracks the wrapper + shell PID. Completion is detected by polling whether the PID is still + alive inside the container. + """ + + def __init__( + self, container_id: str, pid: int, poll_thread: threading.Thread + ) -> None: + self._container_id = container_id + self._pid = pid + self._exit_code: int | None = None + self._error: BaseException | None = None + self._done = threading.Event() + self._poll_thread = poll_thread + + @property + def pid(self) -> int: + return self._pid + + def wait(self, timeout: float | None = None) -> int: + if not self._done.wait(timeout=timeout): + raise TimeoutError( + f"Background command (pid={self._pid}) did not exit within {timeout}s" + ) + if self._error is not None: + raise self._error + return self._exit_code if self._exit_code is not None else 0 + + def kill(self) -> None: + try: + subprocess.run( + ["docker", "exec", self._container_id, "kill", "-9", str(self._pid)], + capture_output=True, + timeout=5, + ) + except Exception: + pass + self._done.set() + + +class DockerSandboxHandle: + """Wraps a running Docker container to satisfy :class:`SandboxHandle`.""" + + def __init__(self, container_id: str, *, user: str | None = None) -> None: + self._container_id = container_id + self._user = user + self._bg_jobs: list[DockerBgJob] = [] + + @property + def sandbox_id(self) -> str: + return self._container_id[:12] + + def exec( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + timeout: float | None = 60, + ) -> ExecResult: + docker_cmd = self._build_exec_cmd(envs=envs, cwd=cwd) + docker_cmd.extend(["bash", "-c", cmd]) + try: + result = subprocess.run( + docker_cmd, + capture_output=True, + text=True, + timeout=timeout, + ) + return ExecResult( + exit_code=result.returncode, + stdout=result.stdout, + stderr=result.stderr, + ) + except subprocess.TimeoutExpired: + return ExecResult( + exit_code=-1, stdout="", stderr=f"Command timed out after {timeout}s" + ) + except Exception as exc: + return ExecResult(exit_code=-1, stdout="", stderr=str(exc)) + + def start_bg( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + ) -> BgJob: + marker = f"/tmp/.bg_{uuid.uuid4().hex[:8]}" + wrapped = f"bash -c {_shell_quote(cmd + f'; echo $? > {marker}')} &\necho $!" + docker_cmd = self._build_exec_cmd(envs=envs, cwd=cwd) + docker_cmd.extend(["bash", "-c", wrapped]) + result = subprocess.run(docker_cmd, capture_output=True, text=True, timeout=10) + if result.returncode != 0: + raise RuntimeError(f"Failed to start background command: {result.stderr}") + pid = int(result.stdout.strip().splitlines()[-1]) + + job = DockerBgJob(self._container_id, pid, poll_thread=None) # type: ignore[arg-type] + poll_thread = threading.Thread( + target=self._poll_bg_job, + args=(job, marker), + daemon=True, + ) + job._poll_thread = poll_thread + self._bg_jobs.append(job) + poll_thread.start() + return job + + def write_text(self, path: str, content: str) -> None: + parent = str(PurePosixPath(path).parent) + if parent not in ("", "/"): + subprocess.run( + ["docker", "exec", self._container_id, "mkdir", "-p", parent], + capture_output=True, + timeout=10, + ) + subprocess.run( + [ + "docker", + "exec", + "-i", + self._container_id, + "bash", + "-c", + f"cat > {_shell_quote(path)}", + ], + input=content.encode(), + capture_output=True, + timeout=30, + ) + + def read_text(self, path: str) -> str: + result = subprocess.run( + ["docker", "exec", self._container_id, "cat", path], + capture_output=True, + text=True, + timeout=30, + ) + if result.returncode != 0: + raise FileNotFoundError(f"No such file in container: {path}") + return result.stdout + + def exists(self, path: str) -> bool: + result = subprocess.run( + ["docker", "exec", self._container_id, "test", "-e", path], + capture_output=True, + timeout=10, + ) + return result.returncode == 0 + + def kill(self) -> None: + for job in self._bg_jobs: + try: + job.kill() + except Exception: + pass + self._bg_jobs.clear() + try: + subprocess.run( + ["docker", "rm", "-f", self._container_id], + capture_output=True, + timeout=15, + ) + except Exception: + pass + + def _build_exec_cmd( + self, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + ) -> list[str]: + cmd = ["docker", "exec"] + if self._user: + cmd.extend(["-u", self._user]) + if cwd: + cmd.extend(["-w", cwd]) + for k, v in (envs or {}).items(): + cmd.extend(["-e", f"{k}={v}"]) + cmd.append(self._container_id) + return cmd + + def _poll_bg_job(self, job: DockerBgJob, marker: str) -> None: + while not job._done.is_set(): + try: + result = subprocess.run( + ["docker", "exec", self._container_id, "cat", marker], + capture_output=True, + text=True, + timeout=5, + ) + if result.returncode == 0 and result.stdout.strip(): + job._exit_code = int(result.stdout.strip()) + job._done.set() + return + except Exception: + pass + + # Also check if PID is gone (crash without writing marker). + try: + check = subprocess.run( + ["docker", "exec", self._container_id, "kill", "-0", str(job._pid)], + capture_output=True, + timeout=5, + ) + if check.returncode != 0: + job._exit_code = 1 + job._done.set() + return + except Exception: + pass + + time.sleep(0.5) + + +class DockerSandboxBackend: + """Creates Docker container sandboxes. + + Each :meth:`create` call spawns a fresh ``docker run -d`` container + that stays alive until :meth:`SandboxHandle.kill` is called or the + container's ``timeout_s`` sleep expires. + """ + + def __init__( + self, + *, + image: str = "ubuntu:22.04", + docker_args: list[str] | None = None, + user: str | None = None, + ) -> None: + self._image = image + self._docker_args = docker_args or [] + self._user = user + + try: + subprocess.run( + ["docker", "version"], + capture_output=True, + check=True, + timeout=5, + ) + except ( + subprocess.CalledProcessError, + FileNotFoundError, + subprocess.TimeoutExpired, + ) as exc: + raise RuntimeError( + "DockerSandboxBackend requires a running Docker daemon." + ) from exc + + def create( + self, + *, + timeout_s: int = 900, + envs: dict[str, str] | None = None, + metadata: dict[str, str] | None = None, + ) -> DockerSandboxHandle: + cmd = [ + "docker", + "run", + "-d", + "--label", + "openenv.sandbox=true", + ] + if metadata: + for k, v in metadata.items(): + cmd.extend(["--label", f"openenv.{k}={v}"]) + for k, v in (envs or {}).items(): + cmd.extend(["-e", f"{k}={v}"]) + cmd.extend(self._docker_args) + cmd.extend([self._image, "sleep", str(timeout_s)]) + + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) + if result.returncode != 0: + raise RuntimeError( + f"Failed to create Docker sandbox: {result.stderr.strip()}" + ) + container_id = result.stdout.strip() + _log.info( + "Docker sandbox created: %s (image=%s)", container_id[:12], self._image + ) + return DockerSandboxHandle(container_id, user=self._user) + + +def _shell_quote(s: str) -> str: + """Single-quote a string for shell, escaping embedded single quotes.""" + return "'" + s.replace("'", "'\\''") + "'" diff --git a/src/openenv/core/harness/sandbox/e2b_backend.py b/src/openenv/core/harness/sandbox/e2b_backend.py index f344346ba..29c9d952d 100644 --- a/src/openenv/core/harness/sandbox/e2b_backend.py +++ b/src/openenv/core/harness/sandbox/e2b_backend.py @@ -21,8 +21,7 @@ from e2b import Sandbox from e2b.sandbox_sync.commands.command_handle import CommandHandle - -from openenv.core.harness.sandbox.base import BgJob, ExecResult, SandboxBackend, SandboxHandle +from openenv.core.harness.sandbox.base import BgJob, ExecResult, SandboxHandle class E2BBgJob: @@ -53,9 +52,7 @@ def pid(self) -> int: def wait(self, timeout: float | None = None) -> int: self._thread.join(timeout) if self._thread.is_alive(): - raise TimeoutError( - f"Background command did not exit within {timeout}s" - ) + raise TimeoutError(f"Background command did not exit within {timeout}s") if self._error is not None: # E2B raises CommandExitException on non-zero; treat as exit code. code = getattr(self._error, "exit_code", None) diff --git a/src/openenv/core/harness/sandbox/interception.py b/src/openenv/core/harness/sandbox/interception.py index dc3dbe5be..d943fd755 100644 --- a/src/openenv/core/harness/sandbox/interception.py +++ b/src/openenv/core/harness/sandbox/interception.py @@ -130,9 +130,7 @@ async def chat_completions(request: Request) -> Response: try: body = json.loads(raw_body) except json.JSONDecodeError: - return JSONResponse( - status_code=400, content={"error": "invalid json body"} - ) + return JSONResponse(status_code=400, content={"error": "invalid json body"}) forwarded_body = _prepare_forwarded_body(body, cfg) headers = { @@ -338,7 +336,7 @@ async def _stream() -> Any: yield line + "\n" if not line.startswith("data:"): continue - data = line[len("data:"):].strip() + data = line[len("data:") :].strip() if data == "[DONE]": continue try: @@ -381,7 +379,11 @@ def _accumulate_stream_chunk(chunk: dict[str, Any], acc: dict[str, Any]) -> None tc_idx = tc.get("index", 0) bucket = acc["tool_calls_by_idx"].setdefault( (idx, tc_idx), - {"id": None, "type": "function", "function": {"name": "", "arguments": ""}}, + { + "id": None, + "type": "function", + "function": {"name": "", "arguments": ""}, + }, ) if tc.get("id"): bucket["id"] = tc["id"] @@ -487,8 +489,7 @@ def _strip_logprobs(response_json: dict[str, Any]) -> dict[str, Any]: choices = out.get("choices") if isinstance(choices, list): out["choices"] = [ - {k: v for k, v in (ch or {}).items() if k != "logprobs"} - for ch in choices + {k: v for k, v in (ch or {}).items() if k != "logprobs"} for ch in choices ] return out @@ -537,9 +538,7 @@ def start(self) -> None: lifespan="on", ) self._server = uvicorn.Server(config) - self._thread = threading.Thread( - target=self._run_server, daemon=True - ) + self._thread = threading.Thread(target=self._run_server, daemon=True) self._thread.start() # Wait for the server to accept connections. deadline = time.time() + 10 diff --git a/tests/core/test_docker_sandbox_backend.py b/tests/core/test_docker_sandbox_backend.py new file mode 100644 index 000000000..b47f6bd4e --- /dev/null +++ b/tests/core/test_docker_sandbox_backend.py @@ -0,0 +1,335 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Tests for the Docker sandbox backend. + +Tests marked ``@pytest.mark.docker`` require a running Docker daemon and +are skipped in CI when Docker is unavailable. They exercise the real +``docker run`` / ``docker exec`` / ``docker rm`` lifecycle. +""" + +from __future__ import annotations + +import subprocess +import time + +import pytest + +_DOCKER_AVAILABLE = False +try: + subprocess.run( + ["docker", "version"], + capture_output=True, + check=True, + timeout=5, + ) + _DOCKER_AVAILABLE = True +except Exception: + pass + +docker = pytest.mark.skipif(not _DOCKER_AVAILABLE, reason="Docker not available") + + +class TestDockerSandboxBackendUnit: + """Unit tests that don't require Docker.""" + + def test_import(self): + from openenv.core.harness.sandbox.docker_backend import ( + DockerBgJob, + DockerSandboxBackend, + DockerSandboxHandle, + ) + + assert DockerSandboxBackend is not None + assert DockerSandboxHandle is not None + assert DockerBgJob is not None + + def test_exported_from_package(self): + from openenv.core.harness.sandbox import ( + DockerBgJob, + DockerSandboxBackend, + DockerSandboxHandle, + ) + + assert DockerSandboxBackend is not None + assert DockerSandboxHandle is not None + assert DockerBgJob is not None + + def test_create_sandbox_backend_factory(self): + from openenv.core.harness.sandbox import create_sandbox_backend + + assert callable(create_sandbox_backend) + + def test_create_sandbox_backend_unknown_raises(self): + from openenv.core.harness.sandbox import create_sandbox_backend + + with pytest.raises(ValueError, match="Unknown sandbox backend"): + create_sandbox_backend("bogus") # type: ignore[arg-type] + + @pytest.mark.skipif(_DOCKER_AVAILABLE, reason="Only test error when Docker missing") + def test_backend_raises_without_docker(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + with pytest.raises(RuntimeError, match="Docker daemon"): + DockerSandboxBackend() + + +@docker +class TestDockerSandboxBackendIntegration: + """Integration tests against a real Docker daemon.""" + + def test_create_and_kill(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + assert sandbox.sandbox_id + assert len(sandbox.sandbox_id) == 12 + finally: + sandbox.kill() + + def test_exec_echo(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + result = sandbox.exec("echo hello world") + assert result.exit_code == 0 + assert "hello world" in result.stdout + finally: + sandbox.kill() + + def test_exec_nonzero_exit(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + result = sandbox.exec("exit 42") + assert result.exit_code == 42 + finally: + sandbox.kill() + + def test_exec_with_env(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + result = sandbox.exec("echo $MY_VAR", envs={"MY_VAR": "test123"}) + assert result.exit_code == 0 + assert "test123" in result.stdout + finally: + sandbox.kill() + + def test_exec_with_cwd(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + result = sandbox.exec("pwd", cwd="/tmp") + assert result.exit_code == 0 + assert "/tmp" in result.stdout + finally: + sandbox.kill() + + def test_write_and_read_text(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + sandbox.write_text("/tmp/test.txt", "hello from test") + content = sandbox.read_text("/tmp/test.txt") + assert content == "hello from test" + finally: + sandbox.kill() + + def test_write_creates_parent_dirs(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + sandbox.write_text("/home/user/deep/nested/file.txt", "nested content") + content = sandbox.read_text("/home/user/deep/nested/file.txt") + assert content == "nested content" + finally: + sandbox.kill() + + def test_write_special_chars(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + text = "line1\nline2\n'quotes' and \"doubles\" and $vars" + sandbox.write_text("/tmp/special.txt", text) + content = sandbox.read_text("/tmp/special.txt") + assert content == text + finally: + sandbox.kill() + + def test_read_missing_file_raises(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + with pytest.raises(FileNotFoundError): + sandbox.read_text("/nonexistent/path.txt") + finally: + sandbox.kill() + + def test_exists(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + assert not sandbox.exists("/tmp/check_me.txt") + sandbox.write_text("/tmp/check_me.txt", "exists") + assert sandbox.exists("/tmp/check_me.txt") + finally: + sandbox.kill() + + def test_start_bg_and_wait(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + job = sandbox.start_bg("sleep 1 && echo done > /tmp/bg_out.txt") + exit_code = job.wait(timeout=10) + assert exit_code == 0 + content = sandbox.read_text("/tmp/bg_out.txt") + assert "done" in content + finally: + sandbox.kill() + + def test_start_bg_kill(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + job = sandbox.start_bg("sleep 300") + time.sleep(0.5) + job.kill() + # Should be able to wait without hanging + exit_code = job.wait(timeout=5) + # Exit code after kill is implementation-defined + assert isinstance(exit_code, int) + finally: + sandbox.kill() + + def test_start_bg_timeout(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + job = sandbox.start_bg("sleep 300") + with pytest.raises(TimeoutError): + job.wait(timeout=1) + job.kill() + finally: + sandbox.kill() + + def test_create_with_envs(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60, envs={"INIT_VAR": "from_create"}) + try: + result = sandbox.exec("echo $INIT_VAR") + assert "from_create" in result.stdout + finally: + sandbox.kill() + + def test_create_with_metadata(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create( + timeout_s=60, + metadata={"episode_id": "ep-123"}, + ) + try: + result = subprocess.run( + [ + "docker", + "inspect", + "--format", + '{{index .Config.Labels "openenv.episode_id"}}', + sandbox._container_id, + ], + capture_output=True, + text=True, + ) + assert "ep-123" in result.stdout + finally: + sandbox.kill() + + def test_factory_creates_docker_backend(self): + from openenv.core.harness.sandbox import create_sandbox_backend + + backend = create_sandbox_backend("docker", image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + result = sandbox.exec("echo ok") + assert result.exit_code == 0 + finally: + sandbox.kill() + + def test_satisfies_sandbox_handle_protocol(self): + from openenv.core.harness.sandbox import SandboxBackend + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + assert isinstance(sandbox, SandboxBackend) or hasattr(sandbox, "exec") + assert hasattr(sandbox, "sandbox_id") + assert hasattr(sandbox, "exec") + assert hasattr(sandbox, "start_bg") + assert hasattr(sandbox, "write_text") + assert hasattr(sandbox, "read_text") + assert hasattr(sandbox, "exists") + assert hasattr(sandbox, "kill") + finally: + sandbox.kill() + + def test_satisfies_sandbox_backend_protocol(self): + from openenv.core.harness.sandbox import SandboxBackend + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + assert issubclass(DockerSandboxBackend, SandboxBackend) + + def test_satisfies_bg_job_protocol(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + try: + job = sandbox.start_bg("sleep 1") + assert hasattr(job, "pid") + assert hasattr(job, "wait") + assert hasattr(job, "kill") + job.kill() + finally: + sandbox.kill() + + def test_kill_is_idempotent(self): + from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend + + backend = DockerSandboxBackend(image="ubuntu:22.04") + sandbox = backend.create(timeout_s=60) + sandbox.kill() + sandbox.kill() # should not raise From 9a350062eabbcf903e1a938939ee9d1cefb79cc8 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 13 May 2026 23:38:58 +0530 Subject: [PATCH 05/79] feat: pi agent adapter - fix agent handling --- src/openenv/core/harness/agents/base.py | 2 +- src/openenv/core/harness/agents/cli_driver.py | 25 ++- src/openenv/core/harness/agents/opencode.py | 71 ++++++--- src/openenv/core/harness/agents/pi.py | 145 ++++++++++++++++++ .../core/harness/sandbox/docker_backend.py | 13 +- 5 files changed, 225 insertions(+), 31 deletions(-) create mode 100644 src/openenv/core/harness/agents/pi.py diff --git a/src/openenv/core/harness/agents/base.py b/src/openenv/core/harness/agents/base.py index 145d3001e..72cc9a6cf 100644 --- a/src/openenv/core/harness/agents/base.py +++ b/src/openenv/core/harness/agents/base.py @@ -41,7 +41,7 @@ class MCPConfigSpec: - ``"cli_flags"`` — the driver passes MCP configuration via CLI flags built by :attr:`CLIAgentSpec.build_command`. - ``"settings_file"`` — write into a global settings file (e.g. - Gemini's ``~/.gemini/settings.json``). + e.g. ``~/.config/agent/settings.json``). """ method: Literal["config_file", "cli_flags", "settings_file"] diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 8e8179889..760218687 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -166,11 +166,23 @@ def collect_artifacts(self) -> dict[str, Any]: if artifact_spec.format == "json": result[name] = json.loads(content) elif artifact_spec.format == "jsonl": - result[name] = [ - json.loads(line) - for line in content.splitlines() - if line.strip() - ] + # Parse valid JSON lines, skip non-JSON preamble + # (e.g. opencode emits database migration messages + # before the first JSON event). + records = [] + for line in content.splitlines(): + line = line.strip() + if not line: + continue + try: + records.append(json.loads(line)) + except json.JSONDecodeError: + _log.debug( + "Skipping non-JSON line in %s: %s", + artifact_spec.path, + line[:120], + ) + result[name] = records else: result[name] = content except Exception: @@ -468,7 +480,8 @@ def _write_mcp_config( home=home, ) mcp_content = self.spec.build_mcp_config(self.spec, [], workdir) - sandbox.write_text(mcp_path, mcp_content) + if mcp_content: + sandbox.write_text(mcp_path, mcp_content) # Agent launch diff --git a/src/openenv/core/harness/agents/opencode.py b/src/openenv/core/harness/agents/opencode.py index b179e9c9f..875882f27 100644 --- a/src/openenv/core/harness/agents/opencode.py +++ b/src/openenv/core/harness/agents/opencode.py @@ -25,9 +25,6 @@ from .base import AgentEvent, ArtifactSpec, CLIAgentSpec, MCPConfigSpec -# Command / config / env builders - - def _build_opencode_command( spec: CLIAgentSpec, config: Any, @@ -44,7 +41,7 @@ def _build_opencode_command( return ( f'export PATH="$HOME/.opencode/bin:$PATH" && ' - f"cd {workdir} && " + f"cd {workdir} && git init -q 2>/dev/null; " f'opencode run {format_flag} "$(cat {instruction_file})" ' f"2>&1 | tee {log_file}" ).strip() @@ -55,22 +52,54 @@ def _build_opencode_mcp_config( tools: list[Any], workdir: str, ) -> str: - """Build the ``opencode.json`` content for the MCP config file.""" + """Build ``opencode.json`` content. + + Returns an empty string so the driver skips writing this file. + The actual config is written via ``spec.files`` using + ``_build_opencode_config_file`` which has access to the rollout + config (base_url, api_key, model). + """ + return "" + + +def _build_opencode_config_file(task: Any, config: Any) -> str: + """Build the full ``opencode.json`` dynamically from config fields.""" + base_url = ( + config.base_url if hasattr(config, "base_url") else "http://127.0.0.1:7000/v1" + ) + api_key = config.api_key if hasattr(config, "api_key") else "intercepted" + model = config.model if hasattr(config, "model") else "model" + timeout = ( + int(config.agent_timeout_s * 1000) + if hasattr(config, "agent_timeout_s") + else 600000 + ) + + # Split model into provider_name/model_id for the opencode config format. + # e.g. "zai-org/GLM-5.1:zai-org" becomes provider "hf", model_id as-is. + provider_name = "default" + model_id = model + if hasattr(config, "provider_name") and config.provider_name: + provider_name = config.provider_name + return json.dumps( { "$schema": "https://opencode.ai/config.json", - "model": "intercepted/model", + "model": f"{provider_name}/{model_id}", "provider": { - "intercepted": { + provider_name: { "npm": "@ai-sdk/openai-compatible", - "name": "Intercepted", + "name": provider_name.title(), "options": { - "baseURL": "http://127.0.0.1:7000/v1", - "apiKey": "intercepted", - "timeout": 600000, + "baseURL": base_url, + "apiKey": api_key, + "timeout": timeout, }, "models": { - "model": {"name": "Intercepted Model"}, + model_id: { + "name": model_id, + "id": model_id, + }, }, } }, @@ -107,12 +136,16 @@ def _parse_opencode_event(line: str) -> AgentEvent | None: return None event_type = data.get("type", "") - if event_type in ("assistant", "message"): + if event_type in ("assistant", "message", "text"): return AgentEvent(type="assistant", data=data, raw=line) elif event_type in ("tool_call", "tool_use"): return AgentEvent(type="tool_call", data=data, raw=line) elif event_type in ("tool_result", "tool_response"): return AgentEvent(type="tool_result", data=data, raw=line) + elif event_type in ("step_start",): + return AgentEvent(type="assistant", data=data, raw=line) + elif event_type in ("step_finish",): + return AgentEvent(type="done", data=data, raw=line) elif event_type == "error": return AgentEvent(type="error", data=data, raw=line) elif event_type in ("done", "complete", "end"): @@ -120,9 +153,6 @@ def _parse_opencode_event(line: str) -> AgentEvent | None: return AgentEvent(type="assistant", data=data, raw=line) -# File resolvers - - def _instruction_file_content(task: Any, config: Any) -> str: return task.instruction if hasattr(task, "instruction") else str(task) @@ -133,9 +163,6 @@ def _system_prompt_content(task: Any, config: Any) -> str | None: return None -# Spec definition - - OPENCODE_SPEC = CLIAgentSpec( name="opencode", install_check_cmd=["/home/user/.opencode/bin/opencode", "--version"], @@ -154,15 +181,16 @@ def _system_prompt_content(task: Any, config: Any) -> str | None: default_timeout_s=900.0, setup=( "set -e && " + "curl -fsSL https://opencode.ai/install | bash && " "mkdir -p /home/user/.config/opencode /home/user/logs/agent " "/home/user/logs/verifier /home/user/task /home/user/workdir && " - "curl -fsSL https://opencode.ai/install | bash && " 'export PATH="$HOME/.opencode/bin:$PATH" && ' "opencode --version" ), files={ "/home/user/task/instruction.md": _instruction_file_content, "/home/user/task/system.md": _system_prompt_content, + "/home/user/.config/opencode/opencode.json": _build_opencode_config_file, }, artifacts={ "agent_log": ArtifactSpec( @@ -181,11 +209,8 @@ def _system_prompt_content(task: Any, config: Any) -> str | None: build_env_vars=_build_opencode_env_vars, ) - -# Auto-register on import register_agent(OPENCODE_SPEC) - __all__ = [ "OPENCODE_SPEC", ] diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py new file mode 100644 index 000000000..7e0fa29c1 --- /dev/null +++ b/src/openenv/core/harness/agents/pi.py @@ -0,0 +1,145 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Pi coding agent adapter. + +Pi runs in print mode for non-interactive harness usage:: + + pi --no-session --no-context-files --provider

--model --thinking off \\ + -p @/home/user/task/instruction.txt 2>&1 | tee /home/user/logs/agent/pi.txt + +The provider and model are passed as CLI flags so the spec's ``env`` dict +only needs auth credentials (``HF_TOKEN``, ``OPENAI_API_KEY``, etc.). + +Registered on import:: + + import openenv.core.harness.agents.pi + # PI_SPEC is now in the registry +""" + +from __future__ import annotations + +import json +import shlex +from typing import Any + +from . import register_agent +from .base import AgentEvent, ArtifactSpec, CLIAgentSpec, MCPConfigSpec + + +def _instruction(task: Any, config: Any) -> str: + return task.instruction if hasattr(task, "instruction") else str(task) + + +def _system_prompt(task: Any, config: Any) -> str | None: + if hasattr(config, "system_prompt") and config.system_prompt: + return config.system_prompt + return None + + +def _build_command( + spec: CLIAgentSpec, + config: Any, + task: Any, + mcp_config_path: str | None, +) -> str: + home = config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" + instruction_file = f"{home}/task/instruction.txt" + log_file = f"{home}/logs/agent/pi.txt" + workdir = f"{home}/workdir" + + provider = "" + if hasattr(config, "provider") and config.provider: + provider = f" --provider {shlex.quote(config.provider)}" + model = "" + if hasattr(config, "model") and config.model: + model = f" --model {shlex.quote(config.model)}" + thinking = " --thinking off" + if hasattr(config, "thinking") and config.thinking: + thinking = f" --thinking {shlex.quote(config.thinking)}" + + return ( + f"cd {workdir} && git init -q 2>/dev/null; " + f"pi --no-session --no-context-files" + f"{provider}{model}{thinking}" + f" -p @{instruction_file}" + f" 2>&1 | tee {log_file}" + ) + + +def _build_mcp_config( + spec: CLIAgentSpec, + tools: list[Any], + workdir: str, +) -> str: + return json.dumps({"mcpServers": {}}, indent=2) + + +def _parse_events(line: str) -> AgentEvent | None: + line = line.strip() + if not line: + return None + try: + data = json.loads(line) + except json.JSONDecodeError: + return AgentEvent(type="assistant", data={"text": line}, raw=line) + + event_type = data.get("type", "") + if event_type in ("assistant", "message", "response"): + return AgentEvent(type="assistant", data=data, raw=line) + if event_type in ("tool_call", "tool_use", "function_call"): + return AgentEvent(type="tool_call", data=data, raw=line) + if event_type in ("tool_result", "tool_response"): + return AgentEvent(type="tool_result", data=data, raw=line) + if event_type in ("thinking", "reasoning"): + return AgentEvent(type="reasoning", data=data, raw=line) + if event_type == "error": + return AgentEvent(type="error", data=data, raw=line) + if event_type in ("done", "complete", "end"): + return AgentEvent(type="done", data=data, raw=line) + return AgentEvent(type="assistant", data=data, raw=line) + + +PI_SPEC = CLIAgentSpec( + name="pi", + install_check_cmd=["pi", "--version"], + base_command=["pi", "--no-session", "--no-context-files"], + mcp_config=MCPConfigSpec( + method="config_file", + path_template="{workdir}/.mcp.json", + ), + supports_logprob_proxy=True, + default_timeout_s=600.0, + setup=( + "set -e && " + "apt-get update -qq && apt-get install -y -qq curl ca-certificates gnupg && " + "curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && " + "apt-get install -y -qq nodejs && " + "curl -fsSL https://pi.dev/install.sh | sh && " + "mkdir -p /home/user/logs/agent /home/user/task /home/user/workdir && " + 'export PATH="$HOME/.local/bin:$HOME/.pi/bin:$PATH" && ' + "pi --version" + ), + files={ + "/home/user/task/instruction.txt": _instruction, + "/home/user/task/system.txt": _system_prompt, + }, + artifacts={ + "agent_log": ArtifactSpec(path="/home/user/logs/agent/pi.txt"), + }, + env={ + "HF_TOKEN": "{api_key}", + "PI_SKIP_VERSION_CHECK": "1", + "PI_TELEMETRY": "0", + }, + build_command=_build_command, + build_mcp_config=_build_mcp_config, + parse_events=_parse_events, +) + +register_agent(PI_SPEC) + +__all__ = ["PI_SPEC"] diff --git a/src/openenv/core/harness/sandbox/docker_backend.py b/src/openenv/core/harness/sandbox/docker_backend.py index aeaacad7c..559817d1b 100644 --- a/src/openenv/core/harness/sandbox/docker_backend.py +++ b/src/openenv/core/harness/sandbox/docker_backend.py @@ -134,7 +134,18 @@ def start_bg( result = subprocess.run(docker_cmd, capture_output=True, text=True, timeout=10) if result.returncode != 0: raise RuntimeError(f"Failed to start background command: {result.stderr}") - pid = int(result.stdout.strip().splitlines()[-1]) + # Extract PID from the last numeric-only line (commands may print + # banners like "Database migration complete." before the PID). + pid_line = None + for line in reversed(result.stdout.strip().splitlines()): + if line.strip().isdigit(): + pid_line = line.strip() + break + if pid_line is None: + raise RuntimeError( + f"Could not extract PID from start_bg output: {result.stdout!r}" + ) + pid = int(pid_line) job = DockerBgJob(self._container_id, pid, poll_thread=None) # type: ignore[arg-type] poll_thread = threading.Thread( From 06df791958922f7d08e35a36a2bab873e7c6f258 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Thu, 14 May 2026 10:44:34 +0530 Subject: [PATCH 06/79] chore: agent specifications and improve interception module ref --- src/openenv/core/harness/agents/cli_driver.py | 2 +- src/openenv/core/harness/agents/opencode.py | 2 +- src/openenv/core/harness/agents/pi.py | 2 ++ src/openenv/core/harness/sandbox/interception.py | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 760218687..42ac460f1 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -52,7 +52,7 @@ # already have it baked in. _PROXY_SOURCE_PATH = Path(__file__).resolve().parents[1] / "sandbox" / "interception.py" -# Verifier type — same as opencode_env's Verifier alias +# Verifier type — callable that checks the agent's work and returns a result Verifier = Callable[..., VerifyResult] diff --git a/src/openenv/core/harness/agents/opencode.py b/src/openenv/core/harness/agents/opencode.py index 875882f27..d0146b008 100644 --- a/src/openenv/core/harness/agents/opencode.py +++ b/src/openenv/core/harness/agents/opencode.py @@ -8,7 +8,7 @@ Expresses the OpenCode harness as a purely declarative :class:`CLIAgentSpec`. All builders (command construction, config generation, env var resolution) -are self-contained with no imports from ``envs/opencode_env/``. +are self-contained with no imports from any environment package. Registered on import:: diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py index 7e0fa29c1..63e2eb0c3 100644 --- a/src/openenv/core/harness/agents/pi.py +++ b/src/openenv/core/harness/agents/pi.py @@ -132,6 +132,8 @@ def _parse_events(line: str) -> AgentEvent | None: }, env={ "HF_TOKEN": "{api_key}", + "OPENAI_API_KEY": "{api_key}", + "OPENAI_BASE_URL": "{base_url}", "PI_SKIP_VERSION_CHECK": "1", "PI_TELEMETRY": "0", }, diff --git a/src/openenv/core/harness/sandbox/interception.py b/src/openenv/core/harness/sandbox/interception.py index d943fd755..4e7c857ac 100644 --- a/src/openenv/core/harness/sandbox/interception.py +++ b/src/openenv/core/harness/sandbox/interception.py @@ -511,7 +511,7 @@ class InterceptionProxy: Used by unit tests and by any in-process driver that wants a short-lived proxy on the local machine. Inside a sandbox we invoke :func:`serve` - directly via ``python -m opencode_env.interception``. + directly via ``python -m openenv.core.harness.sandbox.interception``. """ def __init__(self, cfg: ProxyConfig) -> None: @@ -602,7 +602,7 @@ def read_trace(path: str | os.PathLike) -> list[dict[str, Any]]: def main() -> None: - parser = argparse.ArgumentParser(prog="opencode_env.interception") + parser = argparse.ArgumentParser(prog="openenv.core.harness.sandbox.interception") parser.add_argument("--upstream-url", required=True) parser.add_argument( "--upstream-api-key", From a3c4a3d487dfd4bd9047e6ddadf5bdce8c0e4af3 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Thu, 14 May 2026 11:02:13 +0530 Subject: [PATCH 07/79] feat: add tests for opencode + pi harness adapters --- tests/core/test_cli_agent_driver.py | 51 +++++++++------- tests/core/test_harness_adapters.py | 93 +++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 21 deletions(-) create mode 100644 tests/core/test_harness_adapters.py diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index b26f01d67..29bf06caa 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -22,16 +22,10 @@ from typing import Any import pytest +from openenv.core.harness.sandbox.base import ExecResult, SandboxHandle -# Fake sandbox infrastructure (mirrors test_opencode_env.py pattern) - - -@dataclass -class FakeExecResult: - exit_code: int = 0 - stdout: str = "ok" - stderr: str = "" +# Fake sandbox infrastructure (mirrors test_coding_agent_env.py pattern) @dataclass @@ -75,24 +69,24 @@ def exec( envs: dict[str, str] | None = None, cwd: str | None = None, timeout: float | None = 60, - ) -> FakeExecResult: + ) -> ExecResult: self.executed.append(cmd) if cmd == "echo ok": - return FakeExecResult(exit_code=0, stdout="ok") + return ExecResult(exit_code=0, stdout="ok", stderr="") # install check — only standalone version-check commands (short, just # binary + --version) should be treated as install probes. Multi-part # setup scripts that happen to end with --version should succeed. if "--version" in cmd and len(cmd) < 80 and "&&" not in cmd: if self._install_check_succeeds: - return FakeExecResult(exit_code=0, stdout="1.0.0") - return FakeExecResult(exit_code=127, stderr="not found") + return ExecResult(exit_code=0, stdout="1.0.0", stderr="") + return ExecResult(exit_code=127, stdout="", stderr="not found") # healthz check if "healthz" in cmd: if self._healthz_succeeds: - return FakeExecResult(exit_code=0, stdout='{"status":"ok"}') - return FakeExecResult(exit_code=7, stderr="connection refused") + return ExecResult(exit_code=0, stdout='{"status":"ok"}', stderr="") + return ExecResult(exit_code=7, stdout="", stderr="connection refused") # All other commands succeed - return FakeExecResult(exit_code=0, stdout="") + return ExecResult(exit_code=0, stdout="", stderr="") def start_bg( self, @@ -138,7 +132,7 @@ def create( timeout_s: int = 900, envs: dict[str, str] | None = None, metadata: dict[str, str] | None = None, - ) -> FakeSandbox: + ) -> SandboxHandle: sbx = FakeSandbox( install_check_succeeds=self._install_check_succeeds, healthz_succeeds=self._healthz_succeeds, @@ -252,7 +246,9 @@ def test_cli_agent_spec_full(self): parse_events=lambda line: None, ) assert spec.name == "full-agent" + assert spec.artifacts is not None assert len(spec.artifacts) == 2 + assert spec.files is not None assert callable(spec.files["/dynamic.txt"]) @@ -355,14 +351,14 @@ def test_auto_import_opencode(self): # PR 2.3: CLIAgentDriver / CLIAgentSession / CLIAgentSessionFactory -def _make_test_spec(**overrides: Any) -> Any: +def _make_test_spec(**overrides: Any): from openenv.core.harness.agents.base import ( ArtifactSpec, CLIAgentSpec, MCPConfigSpec, ) - defaults = dict( + defaults: dict[str, Any] = dict( name="test-agent", install_check_cmd=["test-agent", "--version"], base_command=["test-agent", "run", "--json"], @@ -428,6 +424,7 @@ def test_create_session_full_lifecycle(self): assert "test-agent run" in bg_cmd # Verify env vars were resolved + assert bg_envs is not None assert bg_envs["API_KEY"] == "sk-test-key" assert bg_envs["BASE_URL"] == "https://api.example.com/v1" assert bg_envs["MODEL"] == "test-model" @@ -491,6 +488,7 @@ def test_create_session_with_proxy(self): # Agent env should point at proxy agent_cmd, agent_envs = sbx.bg_commands[1] + assert agent_envs is not None assert agent_envs["BASE_URL"] == "http://127.0.0.1:7000/v1" session.close() @@ -819,6 +817,7 @@ def test_spec_fields(self): assert OPENCODE_SPEC.supports_logprob_proxy is True assert OPENCODE_SPEC.default_timeout_s == 900.0 assert OPENCODE_SPEC.mcp_config.method == "config_file" + assert OPENCODE_SPEC.mcp_config.path_template is not None assert "{home}" in OPENCODE_SPEC.mcp_config.path_template assert OPENCODE_SPEC.artifacts is not None assert "agent_log" in OPENCODE_SPEC.artifacts @@ -832,6 +831,7 @@ class OcConfig: sandbox_home: str = "/home/user" run_format: str = "json" + assert OPENCODE_SPEC.build_command is not None cmd = OPENCODE_SPEC.build_command( OPENCODE_SPEC, OcConfig(), @@ -845,18 +845,20 @@ class OcConfig: def test_build_mcp_config(self): from openenv.core.harness.agents.opencode import OPENCODE_SPEC + assert OPENCODE_SPEC.build_mcp_config is not None config_str = OPENCODE_SPEC.build_mcp_config( OPENCODE_SPEC, [], "/home/user/workdir", ) - config = json.loads(config_str) - assert "$schema" in config - assert "provider" in config + # OpenCode returns empty string because the config is written + # via spec.files using _build_opencode_config_file instead. + assert config_str == "" def test_parse_events_assistant(self): from openenv.core.harness.agents.opencode import OPENCODE_SPEC + assert OPENCODE_SPEC.parse_events is not None line = json.dumps({"type": "assistant", "content": "hello"}) event = OPENCODE_SPEC.parse_events(line) assert event is not None @@ -865,6 +867,7 @@ def test_parse_events_assistant(self): def test_parse_events_tool_call(self): from openenv.core.harness.agents.opencode import OPENCODE_SPEC + assert OPENCODE_SPEC.parse_events is not None line = json.dumps({"type": "tool_call", "name": "bash", "args": {}}) event = OPENCODE_SPEC.parse_events(line) assert event is not None @@ -873,6 +876,7 @@ def test_parse_events_tool_call(self): def test_parse_events_error(self): from openenv.core.harness.agents.opencode import OPENCODE_SPEC + assert OPENCODE_SPEC.parse_events is not None line = json.dumps({"type": "error", "message": "boom"}) event = OPENCODE_SPEC.parse_events(line) assert event is not None @@ -881,6 +885,7 @@ def test_parse_events_error(self): def test_parse_events_done(self): from openenv.core.harness.agents.opencode import OPENCODE_SPEC + assert OPENCODE_SPEC.parse_events is not None line = json.dumps({"type": "done"}) event = OPENCODE_SPEC.parse_events(line) assert event is not None @@ -889,6 +894,7 @@ def test_parse_events_done(self): def test_parse_events_invalid_json(self): from openenv.core.harness.agents.opencode import OPENCODE_SPEC + assert OPENCODE_SPEC.parse_events is not None assert OPENCODE_SPEC.parse_events("not json") is None assert OPENCODE_SPEC.parse_events("") is None @@ -897,6 +903,7 @@ def test_build_env_vars(self): config = FakeConfig() config.extra_env = {"EXTRA": "val"} + assert OPENCODE_SPEC.build_env_vars is not None envs = OPENCODE_SPEC.build_env_vars(OPENCODE_SPEC, config) assert envs["OPENAI_BASE_URL"] == "https://api.example.com/v1" assert envs["OPENAI_API_KEY"] == "sk-test-key" @@ -908,6 +915,7 @@ def test_files_instruction_resolver(self): task = FakeTask(instruction="Build a REST API") config = FakeConfig() + assert OPENCODE_SPEC.files is not None instruction_fn = OPENCODE_SPEC.files["/home/user/task/instruction.md"] assert callable(instruction_fn) assert instruction_fn(task, config) == "Build a REST API" @@ -917,6 +925,7 @@ def test_files_system_prompt_resolver(self): task = FakeTask() config = FakeConfig() + assert OPENCODE_SPEC.files is not None system_fn = OPENCODE_SPEC.files["/home/user/task/system.md"] assert callable(system_fn) # No system prompt on FakeConfig → returns None diff --git a/tests/core/test_harness_adapters.py b/tests/core/test_harness_adapters.py new file mode 100644 index 000000000..f5e1dc260 --- /dev/null +++ b/tests/core/test_harness_adapters.py @@ -0,0 +1,93 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Tests for currently implemented harness adapters (OpenCode + Pi).""" + +from __future__ import annotations + +import json +from dataclasses import dataclass, field +from typing import Any + +import pytest + + +@dataclass +class FakeTask: + instruction: str = "Write hello.py" + setup_shell: str | None = None + upload_files: dict[str, str] = field(default_factory=dict) + metadata: dict[str, Any] = field(default_factory=dict) + + +@dataclass +class FakeConfig: + base_url: str = "https://api.example.com/v1" + api_key: str = "sk-test" + model: str = "test-model" + agent_timeout_s: float = 300.0 + sandbox_home: str = "/home/user" + system_prompt: str | None = None + + +class TestPiSpec: + def test_registered(self): + from openenv.core.harness.agents import get_agent_spec + + spec = get_agent_spec("pi") + assert spec.name == "pi" + + def test_fields(self): + from openenv.core.harness.agents.pi import PI_SPEC + + assert PI_SPEC.install_check_cmd == ["pi", "--version"] + assert PI_SPEC.mcp_config.method == "config_file" + assert PI_SPEC.mcp_config.path_template is not None + assert ".mcp.json" in PI_SPEC.mcp_config.path_template + assert PI_SPEC.env is not None + assert "HF_TOKEN" in PI_SPEC.env + assert "PI_SKIP_VERSION_CHECK" in PI_SPEC.env + + def test_build_command(self): + from openenv.core.harness.agents.pi import PI_SPEC + + assert PI_SPEC.build_command is not None + cmd = PI_SPEC.build_command(PI_SPEC, FakeConfig(), FakeTask(), None) + assert "pi --no-session" in cmd + assert "--no-context-files" in cmd + + def test_build_mcp_config(self): + from openenv.core.harness.agents.pi import PI_SPEC + + assert PI_SPEC.build_mcp_config is not None + content = PI_SPEC.build_mcp_config(PI_SPEC, [], "/workdir") + assert "mcpServers" in json.loads(content) + + +class TestOpenCodeSpec: + def test_registered(self): + from openenv.core.harness.agents import get_agent_spec + + spec = get_agent_spec("opencode") + assert spec.name == "opencode" + + +class TestRegistryAutoImport: + @pytest.mark.parametrize("name", ["pi", "opencode"]) + def test_auto_import(self, name): + from openenv.core.harness.agents import get_agent_spec + + spec = get_agent_spec(name) + assert spec.name == name + + def test_list_agents_includes_current(self): + import openenv.core.harness.agents.opencode # noqa: F401 + import openenv.core.harness.agents.pi # noqa: F401 + from openenv.core.harness.agents import list_agents + + agents = list_agents() + for name in ["opencode", "pi"]: + assert name in agents, f"{name} not in {agents}" From 81e37a2c4bced06024bc1bf434b33d36c73f99a8 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Thu, 14 May 2026 13:48:48 +0530 Subject: [PATCH 08/79] chore: migrate opencode_env to coding_agent_env --- docs/source/environments.md | 8 +- docs/source/environments/coding_agent.md | 2 + docs/source/environments/opencode.md | 2 - .../.dockerignore | 0 .../.gitignore | 0 .../README.md | 104 +++---- .../__init__.py | 30 +- .../client.py | 32 ++- .../config.py | 6 +- .../harness.py | 55 ++-- .../models.py | 8 +- .../opencode_runtime.py | 24 +- .../openenv.yaml | 2 +- .../pyproject.toml | 17 +- .../sandbox/__init__.py | 0 .../sandbox/build_template.py | 12 +- .../server/Dockerfile | 6 +- .../server/__init__.py | 2 +- .../server/app.py | 22 +- .../server/catalog.py | 0 .../server/coding_environment.py} | 262 ++++++++++++++---- .../server/gradio_ui.py | 59 ++-- .../task.py | 14 +- .../uv.lock | 70 ++--- ...v_simple.py => coding_agent_env_simple.py} | 24 +- ...encode_env.py => test_coding_agent_env.py} | 140 +++++++--- 26 files changed, 566 insertions(+), 335 deletions(-) create mode 100644 docs/source/environments/coding_agent.md delete mode 100644 docs/source/environments/opencode.md rename envs/{opencode_env => coding_agent_env}/.dockerignore (100%) rename envs/{opencode_env => coding_agent_env}/.gitignore (100%) rename envs/{opencode_env => coding_agent_env}/README.md (67%) rename envs/{opencode_env => coding_agent_env}/__init__.py (63%) rename envs/{opencode_env => coding_agent_env}/client.py (85%) rename envs/{opencode_env => coding_agent_env}/config.py (94%) rename envs/{opencode_env => coding_agent_env}/harness.py (87%) rename envs/{opencode_env => coding_agent_env}/models.py (90%) rename envs/{opencode_env => coding_agent_env}/opencode_runtime.py (86%) rename envs/{opencode_env => coding_agent_env}/openenv.yaml (76%) rename envs/{opencode_env => coding_agent_env}/pyproject.toml (68%) rename envs/{opencode_env => coding_agent_env}/sandbox/__init__.py (100%) rename envs/{opencode_env => coding_agent_env}/sandbox/build_template.py (91%) rename envs/{opencode_env => coding_agent_env}/server/Dockerfile (91%) rename envs/{opencode_env => coding_agent_env}/server/__init__.py (79%) rename envs/{opencode_env => coding_agent_env}/server/app.py (81%) rename envs/{opencode_env => coding_agent_env}/server/catalog.py (100%) rename envs/{opencode_env/server/opencode_environment.py => coding_agent_env/server/coding_environment.py} (70%) rename envs/{opencode_env => coding_agent_env}/server/gradio_ui.py (92%) rename envs/{opencode_env => coding_agent_env}/task.py (73%) rename envs/{opencode_env => coding_agent_env}/uv.lock (99%) rename examples/{opencode_env_simple.py => coding_agent_env_simple.py} (83%) rename tests/envs/{test_opencode_env.py => test_coding_agent_env.py} (73%) diff --git a/docs/source/environments.md b/docs/source/environments.md index a14564a1a..58f36c155 100644 --- a/docs/source/environments.md +++ b/docs/source/environments.md @@ -549,13 +549,13 @@ AgentWorldModel-1K — 1,000 synthetic MCP tool-use environments with 10,000 tas ``` ```` -````{grid-item-card} Opencode +````{grid-item-card} Coding Agent :class-card: sd-border-1 -`opencode_env` runs the OpenCode coding agent inside an isolated E2B sandbox against any OpenAI-compatible LLM endpoint, optionally capturing per-token logpr... +`coding_agent_env` runs coding-agent harnesses (currently OpenCode + Pi) inside an isolated E2B sandbox against any OpenAI-compatible LLM endpoint, optionally capturing per-token logpr... +++ -```{button-link} environments/opencode.html +```{button-link} environments/coding_agent.html :color: primary :outline: @@ -633,5 +633,5 @@ environments/tbench2 environments/unity environments/wildfire environments/agent_world_model -environments/opencode +environments/coding_agent ``` diff --git a/docs/source/environments/coding_agent.md b/docs/source/environments/coding_agent.md new file mode 100644 index 000000000..2903e2322 --- /dev/null +++ b/docs/source/environments/coding_agent.md @@ -0,0 +1,2 @@ +```{include} ../../../envs/coding_agent_env/README.md +``` diff --git a/docs/source/environments/opencode.md b/docs/source/environments/opencode.md deleted file mode 100644 index 9a93ebe33..000000000 --- a/docs/source/environments/opencode.md +++ /dev/null @@ -1,2 +0,0 @@ -```{include} ../../../envs/opencode_env/README.md -``` diff --git a/envs/opencode_env/.dockerignore b/envs/coding_agent_env/.dockerignore similarity index 100% rename from envs/opencode_env/.dockerignore rename to envs/coding_agent_env/.dockerignore diff --git a/envs/opencode_env/.gitignore b/envs/coding_agent_env/.gitignore similarity index 100% rename from envs/opencode_env/.gitignore rename to envs/coding_agent_env/.gitignore diff --git a/envs/opencode_env/README.md b/envs/coding_agent_env/README.md similarity index 67% rename from envs/opencode_env/README.md rename to envs/coding_agent_env/README.md index 79ebc6ed3..11fb88188 100644 --- a/envs/opencode_env/README.md +++ b/envs/coding_agent_env/README.md @@ -1,5 +1,5 @@ --- -title: OpenCode Environment Server +title: Coding Agent Environment Server emoji: 🛠️ colorFrom: indigo colorTo: purple @@ -9,23 +9,24 @@ app_port: 8000 base_path: /web tags: - openenv -short_description: OpenCode coding agent in an E2B sandbox with logprob capture +short_description: Multi-harness coding-agent env (OpenCode + Pi) in E2B with logprob capture --- -# OpenCode Environment for OpenEnv +# Coding Agent Environment for OpenEnv -`opencode_env` runs the [OpenCode](https://opencode.ai) coding agent inside -an isolated [E2B](https://e2b.dev) sandbox against any OpenAI-compatible +`coding_agent_env` runs coding-agent harnesses (currently +[OpenCode](https://opencode.ai) and [Pi](https://github.com/badlogic/pi-mono)) +inside an isolated [E2B](https://e2b.dev) sandbox against any OpenAI-compatible LLM endpoint, optionally capturing per-token logprobs for GRPO training. -**🚀 Try it live**: [`AdithyaSK/opencode-env`](https://huggingface.co/spaces/AdithyaSK/opencode-env) +**🚀 Try it live**: [`AdithyaSK/coding-agent-env`](https://huggingface.co/spaces/AdithyaSK/coding-agent-env) The deployed Space exposes: -- **Web UI** at [`/web`](https://adithyask-opencode-env.hf.space/web) — pick endpoint, write task, hit Run, watch live phase log + reward + logprobs. -- **MCP tool API** at [`/mcp`](https://adithyask-opencode-env.hf.space/mcp) — programmatic `run_rollout` calls. -- **OpenAPI docs** at [`/docs`](https://adithyask-opencode-env.hf.space/docs). -- **Health** at [`/health`](https://adithyask-opencode-env.hf.space/health). +- **Web UI** at [`/web`](https://adithyask-coding-agent-env.hf.space/web) — pick endpoint, write task, hit Run, watch live phase log + reward + logprobs. +- **MCP tool API** at [`/mcp`](https://adithyask-coding-agent-env.hf.space/mcp) — programmatic `run_rollout` calls. +- **OpenAPI docs** at [`/docs`](https://adithyask-coding-agent-env.hf.space/docs). +- **Health** at [`/health`](https://adithyask-coding-agent-env.hf.space/health). The env is **task-agnostic** — every rollout is configured at call-time with a uniform Task shape: @@ -47,20 +48,21 @@ a float to `/home/user/logs/verifier/reward.txt` (override). ```python import asyncio import os -from opencode_env import OpenCodeEnv -from opencode_env.client import _extract_text -from opencode_env.models import RolloutResult +from coding_agent_env import CodingAgentEnv +from coding_agent_env.client import _extract_text +from coding_agent_env.models import RolloutResult async def main(): - SPACE = "https://adithyask-opencode-env.hf.space" + SPACE = "https://adithyask-coding-agent-env.hf.space" - async with OpenCodeEnv(base_url=SPACE) as env: + async with CodingAgentEnv(base_url=SPACE) as env: await env.reset() # The MCP tool returns JSON; deserialize via the typed model. raw = await env.call_tool( "run_rollout", + agent="opencode", # opencode | pi endpoint="openai", # vllm | openai | hf_router api_key=os.environ["OPENAI_API_KEY"], # or set as a Space secret instruction=( @@ -75,7 +77,7 @@ async def main(): "import binary_search; " "assert binary_search.binary_search([1,2,3], 2) == 1; print('OK')\"", ], - template="opencode-rl", # prebaked E2B template + template="coding-agent-rl", # prebaked E2B template task_id="binary_search_v1", ) result = RolloutResult.model_validate_json(_extract_text(raw)) @@ -102,10 +104,10 @@ wall: 19.8 s ```python import os -from opencode_env import OpenCodeEnv +from coding_agent_env import CodingAgentEnv # .sync() returns a synchronous wrapper around the async client. -with OpenCodeEnv(base_url="https://adithyask-opencode-env.hf.space").sync() as env: +with CodingAgentEnv(base_url="https://adithyask-coding-agent-env.hf.space").sync() as env: env.reset() # MCP tools are reachable via env.call_tool(...) / env.step(...) sync-wrapped. # See the async example above for the full run_rollout signature. @@ -120,12 +122,12 @@ For trainers that want to drive a sandbox directly without an HTTP boundary: ```python import os -from opencode_env import ( - OpenCodeConfig, OpenCodeSessionFactory, OpenCodeTask, E2BSandboxBackend, +from coding_agent_env import ( + CodingAgentConfig, CodingAgentSessionFactory, CodingAgentTask, E2BSandboxBackend, ) -factory = OpenCodeSessionFactory( - config=OpenCodeConfig( +factory = CodingAgentSessionFactory( + config=CodingAgentConfig( provider="openai_compatible", base_url="https://api.openai.com/v1", api_key=os.environ["OPENAI_API_KEY"], @@ -134,7 +136,7 @@ factory = OpenCodeSessionFactory( sandbox_backend=E2BSandboxBackend(), mode="transparent_proxy", # captures per-token logprobs ) -session = factory.create(task=OpenCodeTask(instruction="...")) +session = factory.create(task=CodingAgentTask(instruction="...")) session.wait_for_completion() turns = session.fetch_proxy_trace() # per-turn (tokens, logprobs) session.close() @@ -146,22 +148,22 @@ The Dockerfile lives at `server/Dockerfile`. Use the `openenv` CLI from the env root: ```bash -cd envs/opencode_env +cd envs/coding_agent_env openenv validate # check pyproject.toml + openenv.yaml + server/app.py + uv.lock -openenv build -t opencode-env # builds the image (uses server/Dockerfile) +openenv build -t coding-agent-env # builds the image (uses server/Dockerfile) # run locally with E2B credentials -docker run -p 8000:8000 -e E2B_API_KEY=e2b_... opencode-env +docker run -p 8000:8000 -e E2B_API_KEY=e2b_... coding-agent-env # push to HF Spaces (Docker variant) -openenv push --repo-id /opencode-env +openenv push --repo-id /coding-agent-env ``` Or build directly without the CLI: ```bash -docker build -t opencode-env -f envs/opencode_env/server/Dockerfile envs/opencode_env +docker build -t coding-agent-env -f envs/coding_agent_env/server/Dockerfile envs/coding_agent_env ``` The image: @@ -174,7 +176,7 @@ The image: ## The MCP Tool: `run_rollout` -Single tool, two ways to specify the LLM endpoint: +Single tool, with an ``agent`` selector plus two ways to specify the LLM endpoint: **Option A — endpoint shorthand (recommended)**: pass `endpoint="vllm"` (or `"openai"` / `"hf_router"`). The server resolves @@ -186,9 +188,10 @@ directly. | Arg | Type | Default | Notes | |---|---|---|---| +| `agent` | `str` | `"opencode"` | Harness to run: `"opencode"` or `"pi"`. | | `endpoint` | `str` | `""` | One of `"vllm"` / `"openai"` / `"hf_router"`. | | `base_url` / `api_key` / `model` | `str` | `""` | Override / supply explicitly. | -| `instruction` | `str` | required | Prompt passed to `opencode run`. | +| `instruction` | `str` | required | Prompt passed to the selected harness CLI. | | `setup` | `list[str]` | `[]` | Bash commands run **before** the agent. | | `verify` | `list[str]` | `[]` | Bash commands run **after** the agent. | | `task_id` | `str` | `""` | Echoed back in result. | @@ -196,8 +199,8 @@ directly. | `disable_thinking` | `bool \| None` | `None` (catalog default) | Inject `chat_template_kwargs.enable_thinking=false`. | | `max_tokens_cap` | `int` | `4096` | Per-turn `max_tokens` clamp. | | `top_logprobs` | `int` | `5` | HF Router cap is 5; OpenAI 0–20; vLLM unbounded. | -| `agent_timeout_s` | `float` | `600.0` | Hard wall budget for opencode. | -| `template` | `str` | `""` | E2B template name; `"opencode-rl"` skips ~2 min of install per rollout. | +| `agent_timeout_s` | `float` | `600.0` | Hard wall budget for the selected harness. | +| `template` | `str` | `""` | E2B template name; `"coding-agent-rl"` skips ~2 min of install per rollout. | Returns `RolloutResult` JSON with: `reward`, `setup_results[]`, `verify_results[]`, `proxy_turns[]`, `files{}`, `agent_log_tail`, @@ -207,8 +210,8 @@ Returns `RolloutResult` JSON with: `reward`, `setup_results[]`, | Mode | What it does | Best for | |---|---|---| -| **`transparent_proxy`** (default) | In-sandbox proxy at `localhost:7000` forwards opencode's LLM calls to `base_url`, injects `logprobs=true`, captures per-turn `(messages, completion_tokens, logprobs)` to `proxy_trace.jsonl`. | GRPO / RL training, observability, top-k distillation. | -| **`black_box`** | No proxy. opencode talks straight to `base_url`. | Smoke tests, eval, SFT data collection. | +| **`transparent_proxy`** (default) | In-sandbox proxy at `localhost:7000` forwards harness LLM calls to `base_url`, injects `logprobs=true`, captures per-turn `(messages, completion_tokens, logprobs)` to `proxy_trace.jsonl`. | GRPO / RL training, observability, top-k distillation. | +| **`black_box`** | No proxy. The selected harness talks straight to `base_url`. | Smoke tests, eval, SFT data collection. | ## Environment Variables @@ -240,21 +243,21 @@ Hyperbolic / Featherless (silent drop) and Groq (HTTP 400). ## Pre-baked E2B Template The first rollout in a fresh E2B sandbox spends ~2 min installing -opencode and the proxy's Python deps. Build a one-time template that +harness tooling and the proxy's Python deps. Build a one-time template that ships those pre-installed: ```bash -.venv/bin/python envs/opencode_env/sandbox/build_template.py -# → builds `opencode-rl` template in your E2B account (~1m20s, one-time) +.venv/bin/python envs/coding_agent_env/sandbox/build_template.py +# → builds `coding-agent-rl` template in your E2B account (~1m20s, one-time) ``` -After this, pass `template="opencode-rl"` on every `run_rollout` call — +After this, pass `template="coding-agent-rl"` on every `run_rollout` call — each rollout drops to ~20–30s end-to-end. ## Project Structure ``` -opencode_env/ +coding_agent_env/ ├── README.md # this file ├── openenv.yaml # OpenEnv space spec ├── pyproject.toml # deps + ``server`` entrypoint @@ -262,33 +265,38 @@ opencode_env/ ├── .gitignore / .dockerignore # excludes .env / __pycache__ ├── __init__.py # re-exports primitive + client + models │ -├── client.py # OpenCodeEnv(MCPToolClient) -├── models.py # RolloutResult / RolloutTurn / OpenCodeState +├── client.py # CodingAgentEnv(MCPToolClient) +├── models.py # RolloutResult / RolloutTurn / CodingAgentState │ -├── config.py # OpenCodeConfig (primitive) -├── harness.py # OpenCodeSession / OpenCodeSessionFactory (CLI-only) +├── config.py # CodingAgentConfig (primitive) +├── harness.py # CodingAgentSession / CodingAgentSessionFactory (CLI-only) ├── opencode_runtime.py # opencode.json builder + cmds -├── task.py # OpenCodeTask +├── task.py # CodingAgentTask │ ├── server/ │ ├── __init__.py │ ├── app.py # FastAPI factory; mounts Gradio at /web -│ ├── opencode_environment.py # MCPEnvironment with single ``run_rollout`` tool +│ ├── coding_environment.py # MCPEnvironment with single ``run_rollout`` tool │ ├── gradio_ui.py # the /web Gradio Blocks UI │ ├── catalog.py # endpoint shorthand resolver │ └── Dockerfile # multi-stage uv build (used by ``openenv build``) │ └── sandbox/ ├── __init__.py - ├── base.py # SandboxBackend / SandboxHandle Protocols - ├── e2b.py # E2B implementation - ├── interception.py # in-sandbox FastAPI proxy (logprob capture) └── build_template.py # one-time E2B template builder + +# Shared sandbox runtime (moved to core): +src/openenv/core/harness/sandbox/ +├── base.py # SandboxBackend / SandboxHandle protocols +├── e2b_backend.py # E2B implementation +├── docker_backend.py # local Docker backend +└── interception.py # in-sandbox FastAPI proxy (logprob capture) ``` ## References - [OpenEnv docs](https://meta-pytorch.org/OpenEnv/) - [OpenCode CLI](https://opencode.ai/docs/cli/) +- [Pi](https://github.com/badlogic/pi-mono) - [E2B Python SDK](https://e2b.dev/docs) - [HF Inference Providers logprob matrix](../../../DOCS/HF/hf_inference_providers_logprobs.md) diff --git a/envs/opencode_env/__init__.py b/envs/coding_agent_env/__init__.py similarity index 63% rename from envs/opencode_env/__init__.py rename to envs/coding_agent_env/__init__.py index dcd48a01c..6b839e7ea 100644 --- a/envs/opencode_env/__init__.py +++ b/envs/coding_agent_env/__init__.py @@ -4,16 +4,16 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""OpenCode environment for OpenEnv. +"""Coding-agent environment for OpenEnv. Two layers in this package: -1. **Harness primitive** -- :class:`OpenCodeSessionFactory` / - :class:`OpenCodeSession` / :class:`OpenCodeConfig` / +1. **Harness primitive** -- :class:`CodingAgentSessionFactory` / + :class:`CodingAgentSession` / :class:`CodingAgentConfig` / :class:`E2BSandboxBackend`. Built on the generic :class:`CLIAgentDriver` from ``openenv.core.harness.agents``. -2. **Deployable env** -- :class:`OpenCodeEnv` (MCP client) talks to the +2. **Deployable env** -- :class:`CodingAgentEnv` (MCP client) talks to the FastAPI server at ``server/app.py`` over HTTP. Use this when the sandbox + agent live behind an HTTP boundary (e.g. an HF Space). See ``client.py`` and ``server/``. @@ -22,11 +22,11 @@ from openenv.core.env_server.mcp_types import CallToolAction, ListToolsAction from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle -from .client import OpenCodeEnv -from .config import OpenCodeConfig, Provider -from .harness import OpenCodeSession, OpenCodeSessionFactory -from .models import CommandResult, OpenCodeState, RolloutResult, RolloutTurn -from .task import OpenCodeTask +from .client import CodingAgentEnv +from .config import CodingAgentConfig, Provider +from .harness import CodingAgentSession, CodingAgentSessionFactory +from .models import CommandResult, CodingAgentState, RolloutResult, RolloutTurn +from .task import CodingAgentTask try: from openenv.core.harness.sandbox import E2BSandboxBackend @@ -35,19 +35,19 @@ __all__ = [ # Deployed-env client - "OpenCodeEnv", + "CodingAgentEnv", "CallToolAction", "ListToolsAction", # HTTP API models "CommandResult", - "OpenCodeState", + "CodingAgentState", "RolloutResult", "RolloutTurn", # Harness primitive - "OpenCodeConfig", - "OpenCodeSession", - "OpenCodeSessionFactory", - "OpenCodeTask", + "CodingAgentConfig", + "CodingAgentSession", + "CodingAgentSessionFactory", + "CodingAgentTask", "Provider", # Sandbox backend "E2BSandboxBackend", diff --git a/envs/opencode_env/client.py b/envs/coding_agent_env/client.py similarity index 85% rename from envs/opencode_env/client.py rename to envs/coding_agent_env/client.py index 52e76e2d5..8c512090d 100644 --- a/envs/opencode_env/client.py +++ b/envs/coding_agent_env/client.py @@ -4,16 +4,17 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Client for the deployed opencode_env server. +"""Client for the deployed coding_agent_env server. -The server exposes a single MCP tool ``run_rollout`` that runs one OpenCode -rollout in an E2B sandbox and returns a JSON-serialized :class:`RolloutResult`. +The server exposes a single MCP tool ``run_rollout`` that runs one coding-agent +rollout (OpenCode or Pi) in an E2B sandbox and returns a JSON-serialized +:class:`RolloutResult`. Example:: - from opencode_env import OpenCodeEnv + from coding_agent_env import CodingAgentEnv - with OpenCodeEnv(base_url="https://adithya-sk-opencode-env.hf.space") as env: + with CodingAgentEnv(base_url="https://your-space.hf.space") as env: env.reset() result = env.run_rollout( base_url="https://api.openai.com/v1", @@ -40,8 +41,8 @@ from models import RolloutResult # type: ignore -class OpenCodeEnv(MCPToolClient): - """Typed client for the opencode_env MCP server. +class CodingAgentEnv(MCPToolClient): + """Typed client for the coding_agent_env MCP server. Inherits ``reset`` / ``call_tool`` / ``list_tools`` / ``from_docker_image`` / context-manager semantics from :class:`MCPToolClient`. @@ -50,7 +51,8 @@ class OpenCodeEnv(MCPToolClient): def run_rollout( self, *, - # Endpoint — pass either the shorthand selector OR explicit fields. + # Agent + endpoint — pass either shorthand endpoint or explicit fields. + agent: str = "opencode", # "opencode" | "pi" endpoint: str = "", # "vllm" | "openai" | "hf_router" base_url: str = "", api_key: str = "", @@ -68,16 +70,17 @@ def run_rollout( agent_timeout_s: float = 600.0, template: str = "", ) -> RolloutResult: - """Run one OpenCode rollout and return the typed result. + """Run one coding-agent rollout and return the typed result. Args: + agent: Harness CLI to run in sandbox (``"opencode"`` or ``"pi"``). base_url: OpenAI-compatible LLM endpoint (with trailing /v1). api_key: Bearer token for the LLM. Use ``"intercepted"`` for vLLM if it doesn't enforce auth. model: Model id understood by the LLM endpoint (e.g. ``"gpt-4o-mini"``, ``"Qwen/Qwen3.5-4B"``, ``"Qwen/Qwen3-4B-Instruct-2507:nscale"``). - instruction: Prompt passed to ``opencode run``. + instruction: Prompt passed to the selected harness CLI. setup: Bash commands run sequentially **before** the agent starts. Each command runs in the sandbox; non-zero exit aborts setup. verify: Bash commands run sequentially **after** the agent exits. @@ -90,12 +93,11 @@ def run_rollout( ``chat_template_kwargs.enable_thinking=false`` on forwarded requests. Needed for Qwen3.5 vLLM; harmless on Instruct variants; rejected by OpenAI direct. - max_tokens_cap: Clamp on per-turn ``max_tokens``. OpenCode asks - for ~32k by default; gpt-4o-mini caps at 16k. + max_tokens_cap: Clamp on per-turn ``max_tokens``. top_logprobs: Top-k logprobs requested upstream. HF Router caps at 5; OpenAI accepts up to 20; vLLM is unbounded. - agent_timeout_s: Hard wall-clock budget for one ``opencode run``. - template: E2B template name (e.g. ``"opencode-rl"``). Empty + agent_timeout_s: Hard wall-clock budget for one agent run. + template: E2B template name (e.g. ``"coding-agent-rl"``). Empty string uses the default (slow) base image. Returns: @@ -104,6 +106,7 @@ def run_rollout( """ raw = self.call_tool( "run_rollout", + agent=agent, endpoint=endpoint, base_url=base_url, api_key=api_key, @@ -166,3 +169,4 @@ def _extract_text(result: Any) -> str: return text return str(result) + diff --git a/envs/opencode_env/config.py b/envs/coding_agent_env/config.py similarity index 94% rename from envs/opencode_env/config.py rename to envs/coding_agent_env/config.py index 2b6bae0a2..2eac8d16f 100644 --- a/envs/opencode_env/config.py +++ b/envs/coding_agent_env/config.py @@ -4,7 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Configuration model for the OpenCode harness primitive.""" +"""Configuration model for the coding-agent harness primitive.""" from __future__ import annotations @@ -16,8 +16,8 @@ Provider = Literal["openai_compatible", "openai", "anthropic"] -class OpenCodeConfig(BaseModel): - """All configuration required to launch one OpenCode rollout in a sandbox. +class CodingAgentConfig(BaseModel): + """All configuration required to launch one coding-agent rollout in a sandbox. Field names are provider-agnostic. The primitive maps ``provider`` onto the correct ``opencode.json`` provider block (``@ai-sdk/openai-compatible``, diff --git a/envs/opencode_env/harness.py b/envs/coding_agent_env/harness.py similarity index 87% rename from envs/opencode_env/harness.py rename to envs/coding_agent_env/harness.py index 600aafa82..ccbfa2cfc 100644 --- a/envs/opencode_env/harness.py +++ b/envs/coding_agent_env/harness.py @@ -4,16 +4,16 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""OpenCode session factory + session — backed by CLIAgentDriver. +"""Coding-agent session factory + session — backed by CLIAgentDriver. -This module exposes :class:`OpenCodeSession` and -:class:`OpenCodeSessionFactory` built on top of the generic +This module exposes :class:`CodingAgentSession` and +:class:`CodingAgentSessionFactory` built on top of the generic :class:`CLIAgentDriver` / :class:`CLIAgentSession` / :class:`CLIAgentSessionFactory` from ``openenv.core.harness.agents``. -OpenCode-specific configuration (``opencode.json`` generation, provider +Agent-specific (OpenCode spec) configuration (``opencode.json`` generation, provider mapping, tool enable/disable) is handled by -:mod:`opencode_env.opencode_runtime` builders wired into the +:mod:`coding_agent_env.opencode_runtime` builders wired into the :data:`OPENCODE_SPEC` via callable hooks. """ @@ -31,7 +31,7 @@ from openenv.core.harness.agents.opencode import OPENCODE_SPEC from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle -from .config import OpenCodeConfig +from .config import CodingAgentConfig from .opencode_runtime import ( agent_log_path, build_env_vars, @@ -42,7 +42,7 @@ opencode_config_path, system_prompt_path, ) -from .task import OpenCodeTask +from .task import CodingAgentTask # Inside-sandbox proxy paths (Mode B). @@ -61,21 +61,19 @@ ) -class OpenCodeSession(CLIAgentSession): - """One live OpenCode rollout inside a sandbox. +class CodingAgentSession(CLIAgentSession): + """One live coding-agent rollout inside a sandbox. - Extends :class:`CLIAgentSession` with OpenCode-specific convenience - methods (``fetch_trace``, ``wait_for_completion`` with config-aware - timeout). Fully backward-compatible with code that used the old - ``OpenCodeSession`` API. + Extends :class:`CLIAgentSession` with Agent-specific (OpenCode spec) convenience + methods (``fetch_trace``, ``wait_for_completion`` with config-aware timeout). """ def __init__( self, *, sandbox: SandboxHandle, - config: OpenCodeConfig, - task: OpenCodeTask, + config: CodingAgentConfig, + task: CodingAgentTask, verifier: Verifier | None = None, base_url_override: str | None = None, proxy_trace_path: str | None = None, @@ -108,8 +106,7 @@ def wait_for_completion(self, timeout_s: float | None = None) -> int: def start_agent(self) -> None: """Launch ``opencode run`` as a background subprocess in the sandbox. - Provided for backward compatibility — the factory now starts the - agent during ``create()``, so calling this manually is a no-op + The factory starts the agent during ``create()``; this method is a no-op if the agent is already running. """ if self._agent_bg_job is not None: @@ -119,8 +116,8 @@ def start_agent(self) -> None: self._agent_bg_job = self.sandbox.start_bg(cmd, envs=envs) -class OpenCodeSessionFactory(ResourceSessionFactory): - """Produce isolated per-rollout :class:`OpenCodeSession` instances. +class CodingAgentSessionFactory(ResourceSessionFactory): + """Produce isolated per-rollout :class:`CodingAgentSession` instances. The factory owns sandbox provisioning, opencode install, config injection, and (Mode B) proxy startup. Each :meth:`create` call returns a fresh @@ -128,13 +125,13 @@ class OpenCodeSessionFactory(ResourceSessionFactory): Internally delegates to :class:`CLIAgentDriver` for the generic sandbox lifecycle (readiness probing, install retry, proxy startup). - OpenCode-specific config generation uses ``opencode_runtime`` builders. + Agent-specific (OpenCode spec) config generation uses ``opencode_runtime`` builders. """ def __init__( self, *, - config: OpenCodeConfig, + config: CodingAgentConfig, sandbox_backend: SandboxBackend, mode: Literal["black_box", "transparent_proxy"] = "black_box", verifier: Verifier | None = None, @@ -167,12 +164,12 @@ def create( task: Any, seed: int | None = None, episode_id: str | None = None, - ) -> OpenCodeSession: + ) -> CodingAgentSession: import logging _log = logging.getLogger(__name__) - oc_task = OpenCodeTask.coerce(task) + oc_task = CodingAgentTask.coerce(task) sandbox_timeout = int(self._config.agent_timeout_s) + 300 _log.info( @@ -213,7 +210,7 @@ def create( ) _log.info("factory.create: proxy up at %s", base_url_override) # Rewrite opencode.json so opencode points at the proxy. - proxy_cfg = OpenCodeConfig( + proxy_cfg = CodingAgentConfig( **{ **self._config.model_dump(), "provider": "openai_compatible", @@ -225,7 +222,7 @@ def create( build_opencode_json(proxy_cfg), ) - session = OpenCodeSession( + session = CodingAgentSession( sandbox=sandbox, config=self._config, task=oc_task, @@ -244,7 +241,7 @@ def create( def _bootstrap_sandbox( self, sandbox: SandboxHandle, - task: OpenCodeTask, + task: CodingAgentTask, ) -> None: """Install opencode, write config + task files, run optional setup.""" @@ -310,8 +307,8 @@ def _start_proxy( __all__ = [ - "OpenCodeSession", - "OpenCodeSessionFactory", - "OpenCodeTask", + "CodingAgentSession", + "CodingAgentSessionFactory", + "CodingAgentTask", "Verifier", ] diff --git a/envs/opencode_env/models.py b/envs/coding_agent_env/models.py similarity index 90% rename from envs/opencode_env/models.py rename to envs/coding_agent_env/models.py index b218c5f78..3e31962fb 100644 --- a/envs/opencode_env/models.py +++ b/envs/coding_agent_env/models.py @@ -4,11 +4,11 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Pydantic models for the deployed opencode_env HTTP server. +"""Pydantic models for the deployed coding_agent_env HTTP server. The server exposes a single MCP tool ``run_rollout`` that takes a Task (instruction + setup commands + verify commands) plus an LLM endpoint -config, runs one OpenCode rollout end-to-end inside an E2B sandbox, and +config, runs one coding-agent rollout end-to-end inside an E2B sandbox, and returns a :class:`RolloutResult` JSON. """ @@ -80,8 +80,8 @@ class RolloutResult(BaseModel): error: str | None = None -class OpenCodeState(State): - """Per-session env state across calls to one OpenCodeEnvironment instance. +class CodingAgentState(State): + """Per-session env state across calls to one CodingAgentEnvironment instance. Each HTTP session gets its own env (``SUPPORTS_CONCURRENT_SESSIONS=True`` on the server class), so this state is per-session. diff --git a/envs/opencode_env/opencode_runtime.py b/envs/coding_agent_env/opencode_runtime.py similarity index 86% rename from envs/opencode_env/opencode_runtime.py rename to envs/coding_agent_env/opencode_runtime.py index 75fed41e3..49855528b 100644 --- a/envs/opencode_env/opencode_runtime.py +++ b/envs/coding_agent_env/opencode_runtime.py @@ -16,34 +16,34 @@ import json from typing import Any -from .config import OpenCodeConfig, provider_npm_package +from .config import CodingAgentConfig, provider_npm_package -def opencode_config_path(config: OpenCodeConfig) -> str: +def opencode_config_path(config: CodingAgentConfig) -> str: return f"{config.sandbox_home}/.config/opencode/opencode.json" -def instruction_path(config: OpenCodeConfig) -> str: +def instruction_path(config: CodingAgentConfig) -> str: return f"{config.sandbox_home}/task/instruction.md" -def agent_log_path(config: OpenCodeConfig) -> str: +def agent_log_path(config: CodingAgentConfig) -> str: return f"{config.sandbox_home}/logs/agent/opencode.jsonl" -def system_prompt_path(config: OpenCodeConfig) -> str: +def system_prompt_path(config: CodingAgentConfig) -> str: return f"{config.sandbox_home}/task/system.md" -def verifier_reward_path(config: OpenCodeConfig) -> str: +def verifier_reward_path(config: CodingAgentConfig) -> str: return f"{config.sandbox_home}/logs/verifier/reward.txt" -def workdir_path(config: OpenCodeConfig) -> str: +def workdir_path(config: CodingAgentConfig) -> str: return f"{config.sandbox_home}/workdir" -def build_opencode_json(config: OpenCodeConfig) -> str: +def build_opencode_json(config: CodingAgentConfig) -> str: """Return the serialized ``opencode.json`` the sandbox should install. Provider block is keyed by a stable internal name (``intercepted``) so the @@ -79,7 +79,7 @@ def build_opencode_json(config: OpenCodeConfig) -> str: return json.dumps(doc, indent=2) -def build_install_cmd(config: OpenCodeConfig) -> str: +def build_install_cmd(config: CodingAgentConfig) -> str: """Return the shell command that installs OpenCode + ensures PATH. The upstream installer honors ``OPENCODE_VERSION=x.y.z`` for pinning; @@ -99,7 +99,7 @@ def build_install_cmd(config: OpenCodeConfig) -> str: ) -def build_run_cmd(config: OpenCodeConfig) -> str: +def build_run_cmd(config: CodingAgentConfig) -> str: """Return the shell command that launches OpenCode against a task.""" format_flag = "--format json" if config.run_format == "json" else "" @@ -112,7 +112,7 @@ def build_run_cmd(config: OpenCodeConfig) -> str: def build_env_vars( - config: OpenCodeConfig, *, base_url_override: str | None = None + config: CodingAgentConfig, *, base_url_override: str | None = None ) -> dict[str, str]: """Return env vars to set on the OpenCode process. @@ -129,7 +129,7 @@ def build_env_vars( return env -def _build_tools_block(config: OpenCodeConfig) -> dict[str, bool]: +def _build_tools_block(config: CodingAgentConfig) -> dict[str, bool]: """Translate enabled/disabled lists into opencode's ``tools`` map.""" if config.enabled_tools is not None: diff --git a/envs/opencode_env/openenv.yaml b/envs/coding_agent_env/openenv.yaml similarity index 76% rename from envs/opencode_env/openenv.yaml rename to envs/coding_agent_env/openenv.yaml index 2a534a088..be34c3a51 100644 --- a/envs/opencode_env/openenv.yaml +++ b/envs/coding_agent_env/openenv.yaml @@ -1,5 +1,5 @@ spec_version: 1 -name: opencode_env +name: coding_agent_env type: space runtime: fastapi app: server.app:app diff --git a/envs/opencode_env/pyproject.toml b/envs/coding_agent_env/pyproject.toml similarity index 68% rename from envs/opencode_env/pyproject.toml rename to envs/coding_agent_env/pyproject.toml index 50337baa2..276d3e0be 100644 --- a/envs/opencode_env/pyproject.toml +++ b/envs/coding_agent_env/pyproject.toml @@ -9,9 +9,9 @@ requires = ["setuptools>=45", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "openenv-opencode-env" +name = "openenv-coding-agent-env" version = "0.1.0" -description = "OpenCode coding-agent environment for OpenEnv — runs the OpenCode CLI in an E2B sandbox against any OpenAI-compatible LLM, optionally capturing per-token logprobs." +description = "Coding-agent environment for OpenEnv — runs OpenCode/Pi harnesses in an E2B sandbox against OpenAI-compatible LLM endpoints, optionally capturing per-token logprobs." requires-python = ">=3.10" dependencies = [ # Core OpenEnv (server + MCP). 0.3.0 ships the harness runtime. @@ -40,17 +40,16 @@ dev = [ [project.scripts] # Server entrypoint — enables ``uv run --project . server``. -server = "opencode_env.server.app:main" +server = "coding_agent_env.server.app:main" [tool.setuptools] include-package-data = true packages = [ - "opencode_env", - "opencode_env.sandbox", - "opencode_env.server", - "opencode_env.tests", + "coding_agent_env", + "coding_agent_env.sandbox", + "coding_agent_env.server", ] -package-dir = { "opencode_env" = ".", "opencode_env.sandbox" = "sandbox", "opencode_env.server" = "server", "opencode_env.tests" = "tests" } +package-dir = { "coding_agent_env" = ".", "coding_agent_env.sandbox" = "sandbox", "coding_agent_env.server" = "server" } [tool.setuptools.package-data] -opencode_env = ["**/*.md"] +coding_agent_env = ["**/*.md"] diff --git a/envs/opencode_env/sandbox/__init__.py b/envs/coding_agent_env/sandbox/__init__.py similarity index 100% rename from envs/opencode_env/sandbox/__init__.py rename to envs/coding_agent_env/sandbox/__init__.py diff --git a/envs/opencode_env/sandbox/build_template.py b/envs/coding_agent_env/sandbox/build_template.py similarity index 91% rename from envs/opencode_env/sandbox/build_template.py rename to envs/coding_agent_env/sandbox/build_template.py index 6e0ba4f75..e22b30185 100644 --- a/envs/opencode_env/sandbox/build_template.py +++ b/envs/coding_agent_env/sandbox/build_template.py @@ -25,10 +25,10 @@ Usage:: - .venv/bin/python envs/opencode_env/tests/build_e2b_template.py - # → builds (or rebuilds) ``opencode-rl`` template, prints template id + .venv/bin/python envs/coding_agent_env/sandbox/build_template.py + # → builds (or rebuilds) ``coding-agent-rl`` template, prints template id -Then ``test_five_sorts_e2e.py`` will use it via ``--template opencode-rl``. +Then rollout tests can use it via ``--template coding-agent-rl``. Requires ``E2B_API_KEY`` in the environment. First build is ~3-8 min; subsequent builds reuse the cache and can finish in <60s. @@ -113,8 +113,8 @@ def main(argv: list[str] | None = None) -> int: p = argparse.ArgumentParser(prog="build_e2b_template") p.add_argument( "--name", - default="opencode-rl", - help="Template name (default: opencode-rl).", + default="coding-agent-rl", + help="Template name (default: coding-agent-rl)." ) p.add_argument( "--skip-cache", @@ -123,7 +123,7 @@ def main(argv: list[str] | None = None) -> int: ) args = p.parse_args(argv) - _load_env(_REPO_ROOT / "envs" / "opencode_env" / "sandbox" / ".env") + _load_env(_REPO_ROOT / "envs" / "coding_agent_env" / "sandbox" / ".env") if not os.environ.get("E2B_API_KEY"): print("ERROR: E2B_API_KEY required.", file=sys.stderr) return 2 diff --git a/envs/opencode_env/server/Dockerfile b/envs/coding_agent_env/server/Dockerfile similarity index 91% rename from envs/opencode_env/server/Dockerfile rename to envs/coding_agent_env/server/Dockerfile index ad8319423..97e880343 100644 --- a/envs/opencode_env/server/Dockerfile +++ b/envs/coding_agent_env/server/Dockerfile @@ -4,14 +4,14 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. # -# opencode_env Dockerfile — mirrors the standard OpenEnv multi-stage uv +# coding_agent_env Dockerfile — mirrors the standard OpenEnv multi-stage uv # build used by echo_env / repl_env / jupyter_agent. # # Build: -# docker build -t opencode-env . +# docker build -t coding-agent-env . # # Run: -# docker run -p 8000:8000 -e E2B_API_KEY=e2b_... opencode-env +# docker run -p 8000:8000 -e E2B_API_KEY=e2b_... coding-agent-env ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest FROM ${BASE_IMAGE} AS builder diff --git a/envs/opencode_env/server/__init__.py b/envs/coding_agent_env/server/__init__.py similarity index 79% rename from envs/opencode_env/server/__init__.py rename to envs/coding_agent_env/server/__init__.py index 56363edaa..2eac4fb05 100644 --- a/envs/opencode_env/server/__init__.py +++ b/envs/coding_agent_env/server/__init__.py @@ -4,4 +4,4 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Server-side for the deployed opencode_env.""" +"""Server-side for the deployed coding_agent_env.""" diff --git a/envs/opencode_env/server/app.py b/envs/coding_agent_env/server/app.py similarity index 81% rename from envs/opencode_env/server/app.py rename to envs/coding_agent_env/server/app.py index 0757ef229..df40b507f 100644 --- a/envs/opencode_env/server/app.py +++ b/envs/coding_agent_env/server/app.py @@ -4,7 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""FastAPI app for the opencode_env MCP server. +"""FastAPI app for the coding_agent_env MCP server. Mirrors the standard OpenEnv pattern (echo_env / repl_env / jupyter_agent) plus the custom Gradio UI mounted at ``/web`` per the @@ -16,7 +16,7 @@ E2B_API_KEY=... uvicorn server.app:app --host 0.0.0.0 --port 8000 # Docker: - docker run -p 8000:8000 -e E2B_API_KEY=... opencode-env + docker run -p 8000:8000 -e E2B_API_KEY=... coding-agent-env # HF Space: deploys via the root ``Dockerfile``. @@ -58,13 +58,13 @@ def _load_env_file() -> None: from openenv.core.env_server.http_server import create_app from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation - from .gradio_ui import opencode_gradio_builder - from .opencode_environment import OpenCodeEnvironment + from .gradio_ui import coding_agent_gradio_builder + from .coding_environment import CodingAgentEnvironment except ImportError: # pragma: no cover from openenv.core.env_server.http_server import create_app from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation - from server.gradio_ui import opencode_gradio_builder # type: ignore - from server.opencode_environment import OpenCodeEnvironment # type: ignore + from server.gradio_ui import coding_agent_gradio_builder # type: ignore + from server.coding_environment import CodingAgentEnvironment # type: ignore # Always expose the Gradio UI at /web. Set ENABLE_WEB_INTERFACE=false to @@ -80,22 +80,22 @@ def _custom_gradio_builder( title, quick_start_md, ): - """Hand off to ``server.gradio_ui.opencode_gradio_builder``.""" - return opencode_gradio_builder( + """Hand off to ``server.gradio_ui.coding_agent_gradio_builder``.""" + return coding_agent_gradio_builder( web_manager, action_fields, metadata, is_chat_env, - title or "opencode_env", + title or "coding_agent_env", quick_start_md, ) app = create_app( - OpenCodeEnvironment, + CodingAgentEnvironment, CallToolAction, CallToolObservation, - env_name="opencode_env", + env_name="coding_agent_env", max_concurrent_envs=int(os.getenv("MAX_CONCURRENT_ENVS", "4")), gradio_builder=_custom_gradio_builder, ) diff --git a/envs/opencode_env/server/catalog.py b/envs/coding_agent_env/server/catalog.py similarity index 100% rename from envs/opencode_env/server/catalog.py rename to envs/coding_agent_env/server/catalog.py diff --git a/envs/opencode_env/server/opencode_environment.py b/envs/coding_agent_env/server/coding_environment.py similarity index 70% rename from envs/opencode_env/server/opencode_environment.py rename to envs/coding_agent_env/server/coding_environment.py index 638dd5473..3f8eabd13 100644 --- a/envs/opencode_env/server/opencode_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -4,11 +4,11 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""OpenCode MCP environment. +"""Coding-agent MCP environment. -Single MCP tool ``run_rollout`` that takes a uniform Task shape: +Single MCP tool ``run_rollout`` with a uniform task shape: - - ``instruction`` — prompt for the agent + - ``instruction`` — prompt for the selected agent - ``setup`` — bash commands run BEFORE the agent (in the sandbox) - ``verify`` — bash commands run AFTER the agent @@ -28,6 +28,7 @@ from uuid import uuid4 from fastmcp import FastMCP +from pydantic import BaseModel, Field try: from openenv.core.env_server.mcp_environment import MCPEnvironment @@ -40,7 +41,7 @@ from server.catalog import ENDPOINT_KINDS, resolve_endpoint # type: ignore -# One rollout (sandbox cold start + opencode install + opencode run + +# One rollout (sandbox cold start + harness install + agent run + # verifier) typically takes 30-180s; can spike to ~600s under load. Override # OpenEnv's 30s MCP-tool default so the server doesn't cut us off. _RUN_ROLLOUT_TIMEOUT_S = 900.0 @@ -53,9 +54,27 @@ PROXY_LOG = f"{HOME}/logs/agent/proxy.log" AGENT_LOG = f"{HOME}/logs/agent/opencode.jsonl" VERIFY_TIMEOUT_S = 120 +_SUPPORTED_AGENTS = ("opencode", "pi") +_AGENT_LOG_BY_AGENT: dict[str, str] = { + "opencode": f"{HOME}/logs/agent/opencode.jsonl", + "pi": f"{HOME}/logs/agent/pi.txt", +} -class OpenCodeEnvironment(MCPEnvironment): +class _GenericAgentConfig(BaseModel): + """Minimal config shape for CLIAgentSessionFactory-backed agents.""" + + base_url: str + api_key: str + model: str + agent_timeout_s: float = 600.0 + sandbox_home: str = HOME + provider: str | None = None + thinking: str | None = "off" + extra_env: dict[str, str] = Field(default_factory=dict) + + +class CodingAgentEnvironment(MCPEnvironment): """Per-session environment exposing a single ``run_rollout`` MCP tool.""" SUPPORTS_CONCURRENT_SESSIONS = True @@ -65,33 +84,37 @@ def __init__(self) -> None: try: from ..models import ( CommandResult, - OpenCodeState, + CodingAgentState, RolloutResult, RolloutTurn, ) except ImportError: # pragma: no cover from models import ( # type: ignore CommandResult, - OpenCodeState, + CodingAgentState, RolloutResult, RolloutTurn, ) - from opencode_env import ( + from openenv.core.harness.agents import get_agent_spec + from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory + from coding_agent_env import ( E2BSandboxBackend, - OpenCodeConfig, - OpenCodeSessionFactory, - OpenCodeTask, + CodingAgentConfig, + CodingAgentSessionFactory, + CodingAgentTask, ) self._CommandResult = CommandResult self._RolloutResult = RolloutResult self._RolloutTurn = RolloutTurn - self._OpenCodeState = OpenCodeState - self._OpenCodeConfig = OpenCodeConfig - self._OpenCodeSessionFactory = OpenCodeSessionFactory - self._OpenCodeTask = OpenCodeTask + self._CodingAgentState = CodingAgentState + self._CodingAgentConfig = CodingAgentConfig + self._CodingAgentSessionFactory = CodingAgentSessionFactory + self._CodingAgentTask = CodingAgentTask self._E2BSandboxBackend = E2BSandboxBackend + self._CLIAgentSessionFactory = CLIAgentSessionFactory + self._get_agent_spec = get_agent_spec # Don't raise on missing E2B_API_KEY here — OpenEnv's web-interface # layer instantiates the env at import time for schema introspection, @@ -99,12 +122,14 @@ def __init__(self) -> None: # just exploring. The real check happens lazily in # ``_run_rollout_impl`` (any rollout without creds fails fast there # with a clear error in the result payload). - self._state = self._OpenCodeState(episode_id=str(uuid4())) + self._state = self._CodingAgentState(episode_id=str(uuid4())) - mcp = FastMCP("opencode_env") + mcp = FastMCP("coding_agent_env") @mcp.tool def run_rollout( + # Agent + endpoint. + agent: str = "opencode", # Endpoint — either a shorthand (resolved from env vars + catalog # defaults) OR explicit base_url+api_key+model. Explicit fields # always win over the catalog. @@ -125,14 +150,17 @@ def run_rollout( agent_timeout_s: float = 600.0, template: str = "", ) -> str: - """Run one OpenCode rollout end-to-end. + """Run one coding-agent rollout end-to-end. + + ``agent`` selects the harness CLI to run inside the sandbox. + Currently supported: ``"opencode"``, ``"pi"``. ``endpoint`` is the shorthand selector (one of ``"vllm"`` / ``"openai"`` / ``"hf_router"``) — the server resolves base_url / api_key / model from env vars + catalog defaults. Pass any of those explicitly to override. - See ``opencode_env.client.OpenCodeEnv.run_rollout`` for full + See ``coding_agent_env.client.CodingAgentEnv.run_rollout`` for full arg docs. Returns a JSON-serialized ``RolloutResult``. """ # Resolve via catalog when shorthand is provided. @@ -149,6 +177,11 @@ def run_rollout( if disable_thinking_resolved is None: disable_thinking_resolved = False + agent = (agent or "opencode").strip() + if agent not in _SUPPORTED_AGENTS: + raise ValueError( + f"unsupported agent {agent!r}; supported agents: {_SUPPORTED_AGENTS}" + ) if not (base_url and api_key and model): raise ValueError( "must provide either ``endpoint`` (one of " @@ -158,6 +191,7 @@ def run_rollout( raise ValueError("instruction is required") return self._run_rollout_impl( + agent=agent, base_url=base_url, api_key=api_key, model=model, @@ -183,13 +217,15 @@ def reset( episode_id: Optional[str] = None, **_: Any, ) -> Observation: - self._state = self._OpenCodeState(episode_id=episode_id or str(uuid4())) + self._state = self._CodingAgentState(episode_id=episode_id or str(uuid4())) return Observation( done=False, reward=None, metadata={ "status": "ready", - "message": ("opencode_env ready. Call run_rollout(...) with a task."), + "message": ( + "coding_agent_env ready. Call run_rollout(agent=..., ...) with a task." + ), }, ) @@ -239,6 +275,7 @@ def state(self) -> Any: def _run_rollout_impl( self, *, + agent: str, base_url: str, api_key: str, model: str, @@ -279,19 +316,18 @@ def _emit(msg: str) -> None: _emit("error: E2B_API_KEY missing on server") return result.model_dump_json() - _emit(f"resolving config (model={model}, mode={mode})") + _emit(f"resolving config (agent={agent}, model={model}, mode={mode})") - # Build OpenCodeConfig + factory. We keep the proxy in charge of - # ``model_override`` / ``logprobs`` / ``max_tokens``-cap injection. - config = self._OpenCodeConfig( - provider="openai_compatible", - base_url=base_url.rstrip("/"), + config = self._build_agent_config( + agent=agent, + mode=mode, + base_url=base_url, api_key=api_key, model=model, agent_timeout_s=agent_timeout_s, - proxy_disable_thinking=disable_thinking, - proxy_top_logprobs=top_logprobs, - proxy_max_tokens_cap=max_tokens_cap if max_tokens_cap > 0 else None, + disable_thinking=disable_thinking, + top_logprobs=top_logprobs, + max_tokens_cap=max_tokens_cap, ) # Concatenate setup commands into a single ``set -e`` script and let @@ -300,21 +336,19 @@ def _emit(msg: str) -> None: # each command in a wrapper that captures exit/stdout/stderr. # That way the primitive still aborts on setup failure AND we get # observability in the response. - instruction_payload = instruction - opencode_task = self._OpenCodeTask( - instruction=instruction_payload, - metadata={"task_id": task_id}, + rollout_task = self._CodingAgentTask( + instruction=instruction, + metadata={"task_id": task_id, "agent": agent}, ) - backend_kwargs: dict[str, Any] = {} - if template: - backend_kwargs["template"] = template - - factory = self._OpenCodeSessionFactory( + factory = self._build_session_factory( + agent=agent, config=config, - sandbox_backend=self._E2BSandboxBackend(**backend_kwargs), mode=mode, - verifier=None, + template=template, + disable_thinking=disable_thinking, + top_logprobs=top_logprobs, + max_tokens_cap=max_tokens_cap, ) session = None @@ -323,7 +357,7 @@ def _emit(msg: str) -> None: f"creating E2B sandbox (template={template or 'default'}) — " "this is the slow phase (~5–60s cold, ~5s with template)" ) - session = factory.create(task=opencode_task) + session = factory.create(task=rollout_task) result.sandbox_id = session.sandbox.sandbox_id _emit( f"sandbox ready: {result.sandbox_id} — agent started " @@ -336,7 +370,7 @@ def _emit(msg: str) -> None: # we'd need to restructure. As a pragmatic compromise we run # setup IMMEDIATELY after create(), which races with the agent # for ~1-2s but is fine for typical pip/git/download work - # because opencode itself takes >=20s to make its first model + # because most agent CLIs take a while before their first model # call. for i, cmd in enumerate(setup, 1): _emit(f"setup [{i}/{len(setup)}]: {cmd[:80]}") @@ -352,7 +386,7 @@ def _emit(msg: str) -> None: # Block until the agent is done (or setup already failed). if result.error is None: _emit( - f"agent running — opencode CLI in sandbox " + f"agent running — {agent} CLI in sandbox " f"(timeout {int(agent_timeout_s)}s)" ) try: @@ -387,7 +421,7 @@ def _emit(msg: str) -> None: result.files, result.files_extra = self._collect_files(session.sandbox) result.proxy_turns = self._collect_proxy_turns(session) result.proxy_log_tail = self._safe_read(session.sandbox, PROXY_LOG)[-2000:] - result.agent_log_tail = self._safe_read(session.sandbox, AGENT_LOG)[-2000:] + result.agent_log_tail = self._collect_agent_log_tail(session, agent) _emit( f"collected: {len(result.files)} file(s), " f"{len(result.proxy_turns)} proxy turn(s), " @@ -400,9 +434,7 @@ def _emit(msg: str) -> None: result.proxy_log_tail = self._safe_read(session.sandbox, PROXY_LOG)[ -2000: ] - result.agent_log_tail = self._safe_read(session.sandbox, AGENT_LOG)[ - -2000: - ] + result.agent_log_tail = self._collect_agent_log_tail(session, agent) finally: if session is not None: try: @@ -422,6 +454,104 @@ def _emit(msg: str) -> None: return result.model_dump_json() + def _build_agent_config( + self, + *, + agent: str, + mode: str, + base_url: str, + api_key: str, + model: str, + agent_timeout_s: float, + disable_thinking: bool, + top_logprobs: int, + max_tokens_cap: int, + ) -> Any: + if agent == "opencode": + return self._CodingAgentConfig( + provider="openai_compatible", + base_url=base_url.rstrip("/"), + api_key=api_key, + model=model, + agent_timeout_s=agent_timeout_s, + proxy_disable_thinking=disable_thinking, + proxy_top_logprobs=top_logprobs, + proxy_max_tokens_cap=max_tokens_cap if max_tokens_cap > 0 else None, + ) + + provider = ( + "openai" if mode == "transparent_proxy" else self._infer_pi_provider(base_url) + ) + return _GenericAgentConfig( + base_url=base_url.rstrip("/"), + api_key=api_key, + model=model, + agent_timeout_s=agent_timeout_s, + provider=provider, + thinking="off" if disable_thinking else None, + ) + + def _build_session_factory( + self, + *, + agent: str, + config: Any, + mode: str, + template: str, + disable_thinking: bool, + top_logprobs: int, + max_tokens_cap: int, + ) -> Any: + backend_kwargs: dict[str, Any] = {} + if template: + backend_kwargs["template"] = template + backend = self._E2BSandboxBackend(**backend_kwargs) + + if agent == "opencode": + return self._CodingAgentSessionFactory( + config=config, + sandbox_backend=backend, + mode=mode, + verifier=None, + ) + + spec = self._get_agent_spec(agent) + return self._CLIAgentSessionFactory( + spec=spec, + config=config, + sandbox_backend=backend, + mode=mode, + verifier=None, + proxy_disable_thinking=disable_thinking, + proxy_top_logprobs=top_logprobs, + proxy_max_tokens_cap=max_tokens_cap if max_tokens_cap > 0 else None, + ) + + @staticmethod + def _infer_pi_provider(base_url: str) -> str: + url = (base_url or "").lower() + if "router.huggingface.co" in url: + return "huggingface" + if "anthropic" in url: + return "anthropic" + if "googleapis.com" in url or "generativelanguage" in url: + return "gemini" + return "openai" + + def _collect_agent_log_tail(self, session: Any, agent: str) -> str: + if hasattr(session, "collect_artifacts"): + try: + artifacts = session.collect_artifacts() + if isinstance(artifacts, dict) and "agent_log" in artifacts: + val = artifacts["agent_log"] + if isinstance(val, str): + return val[-2000:] + return json.dumps(val, default=str)[-2000:] + except Exception: + pass + path = _AGENT_LOG_BY_AGENT.get(agent, AGENT_LOG) + return self._safe_read(session.sandbox, path)[-2000:] + # ── Helpers ──────────────────────────────────────────────────────────── def _exec_command(self, sandbox: Any, cmd: str) -> Any: @@ -471,18 +601,33 @@ def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: def _collect_proxy_turns(self, session: Any) -> list[Any]: turns: list[Any] = [] - proxy_trace_path = getattr(session, "_proxy_trace_path", None) - if not proxy_trace_path: - return turns - raw = self._safe_read(session.sandbox, proxy_trace_path) - for line in raw.splitlines(): - line = line.strip() - if not line: - continue + + records: list[dict[str, Any]] = [] + if hasattr(session, "fetch_proxy_trace"): try: - rec = json.loads(line) + fetched = session.fetch_proxy_trace() + if isinstance(fetched, list): + records = [r for r in fetched if isinstance(r, dict)] except Exception: - continue + records = [] + + if not records: + proxy_trace_path = getattr(session, "_proxy_trace_path", None) + if not proxy_trace_path: + return turns + raw = self._safe_read(session.sandbox, proxy_trace_path) + for line in raw.splitlines(): + line = line.strip() + if not line: + continue + try: + rec = json.loads(line) + except Exception: + continue + if isinstance(rec, dict): + records.append(rec) + + for rec in records: response = rec.get("response") or {} turns.append( self._RolloutTurn( @@ -509,3 +654,4 @@ def _safe_read(sandbox: Any, path: str) -> str: return sandbox.read_text(path) or "" except Exception: return "" + diff --git a/envs/opencode_env/server/gradio_ui.py b/envs/coding_agent_env/server/gradio_ui.py similarity index 92% rename from envs/opencode_env/server/gradio_ui.py rename to envs/coding_agent_env/server/gradio_ui.py index d1ee6e403..5497ef0f2 100644 --- a/envs/opencode_env/server/gradio_ui.py +++ b/envs/coding_agent_env/server/gradio_ui.py @@ -4,7 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Minimal Gradio UI for opencode_env. +"""Minimal Gradio UI for coding_agent_env. Mounts under the standard OpenEnv ``/web`` path via the ``gradio_builder=`` callback documented at @@ -32,14 +32,14 @@ try: from .catalog import catalog_summary, ENDPOINT_KINDS, resolve_endpoint - from .opencode_environment import OpenCodeEnvironment + from .coding_environment import CodingAgentEnvironment except ImportError: # pragma: no cover from server.catalog import ( # type: ignore catalog_summary, ENDPOINT_KINDS, resolve_endpoint, ) - from server.opencode_environment import OpenCodeEnvironment # type: ignore + from server.coding_environment import CodingAgentEnvironment # type: ignore # ──────────────────────────────────────────────────────────────────────────── @@ -190,8 +190,8 @@ def _logprobs_md(turns: list[dict[str, Any]]) -> str: toks = first["completion_tokens"][:10] lps = first.get("per_token_logps") or [] lines.append( - f"\n**first productive turn (first 10 tokens)**\n\n" - f"```\n" + "\n**first productive turn (first 10 tokens)**\n\n" + "```\n" + "\n".join( f" {tok!r:<14} {lp:+.3f}" if i < len(lps) else f" {tok!r:<14} -" for i, (tok, lp) in enumerate(zip(toks, lps + [None] * len(toks))) @@ -202,6 +202,7 @@ def _logprobs_md(turns: list[dict[str, Any]]) -> str: def _live_status_md( + agent: str, endpoint_kind: str, model: str, mode: str, @@ -211,7 +212,7 @@ def _live_status_md( """Render a live phase log (latest at the bottom) with elapsed timestamps.""" head = ( f"### running… `elapsed={elapsed_s:.1f}s`\n\n" - f"_endpoint=`{endpoint_kind}` model=`{model}` mode=`{mode}`_\n\n" + f"_agent=`{agent}` endpoint=`{endpoint_kind}` model=`{model}` mode=`{mode}`_\n\n" ) if not lines: body = "_(waiting for first phase update…)_" @@ -255,7 +256,7 @@ def _catalog_banner() -> str: # ──────────────────────────────────────────────────────────────────────────── -def opencode_gradio_builder( +def coding_agent_gradio_builder( web_manager, # noqa: ARG001 (unused: we instantiate the env directly) action_fields, # noqa: ARG001 metadata, # noqa: ARG001 @@ -263,16 +264,17 @@ def opencode_gradio_builder( title, quick_start_md, # noqa: ARG001 ) -> gr.Blocks: - """Build the opencode_env console. + """Build the coding_agent_env console. Compatible with ``create_app(..., gradio_builder=...)``. We ignore - ``web_manager`` and instantiate :class:`OpenCodeEnvironment` ourselves - inside the run handler — opencode_env's run_rollout doesn't need any + ``web_manager`` and instantiate :class:`CodingAgentEnvironment` ourselves + inside the run handler — coding_agent_env's run_rollout doesn't need any per-session state beyond the env's own bookkeeping, and instantiating is cheap (no sandbox is created until the tool fires). """ def run( + agent: str, endpoint: str, model: str, base_url: str, @@ -317,7 +319,7 @@ def run( else: dt = None - env = OpenCodeEnvironment() + env = CodingAgentEnvironment() # The worker fires _run_rollout_impl in a background thread and # streams progress messages into a queue; this generator polls the @@ -331,6 +333,7 @@ def _cb(msg: str) -> None: def _worker(): try: payload = env._run_rollout_impl( + agent=agent, base_url=resolved.base_url, api_key=resolved.api_key, model=resolved.model, @@ -361,7 +364,7 @@ def _worker(): # First yield: announce we've started. Empty result panels. yield ( - f"### running…\n\n_endpoint=`{resolved.kind}` model=`{resolved.model}` mode=`{mode}`_", + f"### running…\n\n_agent=`{agent}` endpoint=`{resolved.kind}` model=`{resolved.model}` mode=`{mode}`_", [], [], "", @@ -387,7 +390,12 @@ def _worker(): # Render the live status pane. elapsed = time.time() - t_start md = _live_status_md( - resolved.kind, resolved.model, mode, elapsed, status_lines + agent, + resolved.kind, + resolved.model, + mode, + elapsed, + status_lines, ) yield (md, [], [], "", "", "", {}) @@ -409,6 +417,7 @@ def _worker(): "", "", _live_status_md( + agent, resolved.kind, resolved.model, mode, @@ -427,8 +436,9 @@ def _worker(): _files_md(result.get("files") or {}), _logprobs_md(result.get("proxy_turns") or []), ( - f"### live phase log\n\n" + "### live phase log\n\n" + _live_status_md( + agent, resolved.kind, resolved.model, mode, @@ -445,17 +455,24 @@ def apply_preset(name: str) -> tuple[str, str, str]: p = PRESETS.get(name) or {"instruction": "", "setup": "", "verify": ""} return p["instruction"], p["setup"], p["verify"] - with gr.Blocks(title=title or "opencode_env") as app: - gr.Markdown(f"# {title or 'opencode_env'}") + with gr.Blocks(title=title or "coding_agent_env") as app: + gr.Markdown(f"# {title or 'coding_agent_env'}") gr.Markdown( - "Run one OpenCode rollout in an E2B sandbox against your chosen " - "LLM endpoint. Pick an endpoint, write the task as `(instruction, " - "setup, verify)`, and inspect the reward + per-token logprobs." + "Run one coding-agent rollout in an E2B sandbox against your chosen " + "LLM endpoint. Pick an agent + endpoint, write the task as " + "`(instruction, setup, verify)`, and inspect reward + per-token " + "logprobs." ) gr.Markdown(_catalog_banner()) with gr.Row(): + agent = gr.Dropdown( + choices=["opencode", "pi"], + value="opencode", + label="Agent", + scale=1, + ) endpoint = gr.Dropdown( choices=list(ENDPOINT_KINDS), value="openai", @@ -481,7 +498,7 @@ def apply_preset(name: str) -> tuple[str, str, str]: ) instruction = gr.Textbox( - label="Instruction (the prompt opencode runs)", + label="Instruction (the prompt the selected agent runs)", lines=4, value=PRESETS["binary_search"]["instruction"], ) @@ -567,6 +584,7 @@ def apply_preset(name: str) -> tuple[str, str, str]: run_btn.click( fn=run, inputs=[ + agent, endpoint, model, base_url, @@ -593,3 +611,4 @@ def apply_preset(name: str) -> tuple[str, str, str]: ) return app + diff --git a/envs/opencode_env/task.py b/envs/coding_agent_env/task.py similarity index 73% rename from envs/opencode_env/task.py rename to envs/coding_agent_env/task.py index f9d208d84..8633eb7aa 100644 --- a/envs/opencode_env/task.py +++ b/envs/coding_agent_env/task.py @@ -4,7 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Task payload accepted by :class:`OpenCodeSessionFactory`.""" +"""Task payload accepted by :class:`CodingAgentSessionFactory`.""" from __future__ import annotations @@ -13,8 +13,8 @@ from pydantic import BaseModel, Field -class OpenCodeTask(BaseModel): - """One task for an OpenCode rollout. +class CodingAgentTask(BaseModel): + """One task for a coding-agent rollout. The primitive only needs ``instruction`` (the prompt handed to ``opencode run``). Callers may attach ``setup_shell`` (run once inside the sandbox @@ -29,8 +29,8 @@ class OpenCodeTask(BaseModel): metadata: dict[str, Any] = Field(default_factory=dict) @classmethod - def coerce(cls, value: Any) -> "OpenCodeTask": - """Accept a bare string, a dict, or an existing ``OpenCodeTask``.""" + def coerce(cls, value: Any) -> "CodingAgentTask": + """Accept a bare string, a dict, or an existing ``CodingAgentTask``.""" if isinstance(value, cls): return value if isinstance(value, str): @@ -38,6 +38,6 @@ def coerce(cls, value: Any) -> "OpenCodeTask": if isinstance(value, dict): return cls(**value) raise TypeError( - f"Cannot coerce {type(value).__name__} to OpenCodeTask; " - "pass a str, dict, or OpenCodeTask." + f"Cannot coerce {type(value).__name__} to CodingAgentTask; " + "pass a str, dict, or CodingAgentTask." ) diff --git a/envs/opencode_env/uv.lock b/envs/coding_agent_env/uv.lock similarity index 99% rename from envs/opencode_env/uv.lock rename to envs/coding_agent_env/uv.lock index 80dd00ba0..aa35531cc 100644 --- a/envs/opencode_env/uv.lock +++ b/envs/coding_agent_env/uv.lock @@ -1664,38 +1664,7 @@ wheels = [ ] [[package]] -name = "openenv-core" -version = "0.2.3" -source = { git = "https://github.com/adithya-s-k/OpenEnv.git?rev=opencode-harness#aabcdbb9d52aa62a842ec69472b2a1106acb831a" } -dependencies = [ - { name = "fastapi" }, - { name = "fastmcp" }, - { name = "gradio" }, - { name = "httpx" }, - { name = "huggingface-hub" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "rich" }, - { name = "tomli" }, - { name = "tomli-w" }, - { name = "typer" }, - { name = "uvicorn" }, - { name = "websockets" }, -] - -[package.optional-dependencies] -core = [ - { name = "fastapi" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "uvicorn" }, - { name = "websockets" }, -] - -[[package]] -name = "openenv-opencode-env" +name = "openenv-coding-agent-env" version = "0.1.0" source = { editable = "." } dependencies = [ @@ -1724,7 +1693,7 @@ requires-dist = [ { name = "fastmcp", specifier = ">=2.0.0" }, { name = "gradio", specifier = ">=6.0.0" }, { name = "httpx", specifier = ">=0.27.0" }, - { name = "openenv-core", extras = ["core"], git = "https://github.com/adithya-s-k/OpenEnv.git?rev=opencode-harness" }, + { name = "openenv-core", extras = ["core"], specifier = ">=0.3.0" }, { name = "pydantic", specifier = ">=2.0.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, @@ -1734,6 +1703,41 @@ requires-dist = [ ] provides-extras = ["dev"] +[[package]] +name = "openenv-core" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "gradio" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, + { name = "tomli" }, + { name = "tomli-w" }, + { name = "typer" }, + { name = "uvicorn" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/d6/3bebe8afb55fcc3ea9251c4c2dfbab2879e31089bc91a8fe9696e5ce019b/openenv_core-0.3.0.tar.gz", hash = "sha256:c7fee2035badab5be497eb6f4afb2cb417de000f82cc19afd72fb5ec332c431d", size = 164720, upload-time = "2026-05-11T11:37:57.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/f5/aafa43138589bfd5d369a8d02ea365aae9d6fe55ac0b3894368d6d69bd03/openenv_core-0.3.0-py3-none-any.whl", hash = "sha256:859e875c9d5211b157c30fb9abc681606fcf0bf1b6ffcdf404678992823a1df0", size = 194313, upload-time = "2026-05-11T11:37:55.537Z" }, +] + +[package.optional-dependencies] +core = [ + { name = "fastapi" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "uvicorn" }, + { name = "websockets" }, +] + [[package]] name = "opentelemetry-api" version = "1.41.1" diff --git a/examples/opencode_env_simple.py b/examples/coding_agent_env_simple.py similarity index 83% rename from examples/opencode_env_simple.py rename to examples/coding_agent_env_simple.py index 1713880fb..f8996e586 100644 --- a/examples/opencode_env_simple.py +++ b/examples/coding_agent_env_simple.py @@ -5,18 +5,18 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""End-to-end opencode_env example: write binary_search.py and verify it. +"""End-to-end coding_agent_env example: write binary_search.py and verify it. -Hits the deployed HF Space ``AdithyaSK/opencode-env`` (override via -``OPENCODE_ENV_SPACE`` env var to point at your own Space or a local +Hits the deployed HF Space ``AdithyaSK/coding-agent-env`` (override via +``CODING_AGENT_ENV_SPACE`` env var to point at your own Space or a local container). The single MCP tool ``run_rollout`` does: - 1. Spawns a fresh E2B sandbox (using the prebaked ``opencode-rl`` + 1. Spawns a fresh E2B sandbox (using the prebaked ``coding-agent-rl`` template — falls back to a cold install if the template isn't present in your E2B account). 2. Bootstraps an in-sandbox FastAPI proxy that captures per-token logprobs (``mode="transparent_proxy"``). - 3. Runs ``opencode run`` with the instruction. + 3. Runs the selected harness CLI with the instruction. 4. Executes the verify bash commands; reward = passed / total. 5. Returns a ``RolloutResult`` with reward + per-turn logprobs + the file contents the agent produced. @@ -29,7 +29,7 @@ Usage:: - PYTHONPATH=src:envs uv run python examples/opencode_env_simple.py + PYTHONPATH=src:envs uv run python examples/coding_agent_env_simple.py Expected output (~20s with the prebaked template):: @@ -49,12 +49,12 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "envs")) -from opencode_env import OpenCodeEnv # noqa: E402 -from opencode_env.client import _extract_text # noqa: E402 -from opencode_env.models import RolloutResult # noqa: E402 +from coding_agent_env import CodingAgentEnv # noqa: E402 +from coding_agent_env.client import _extract_text # noqa: E402 +from coding_agent_env.models import RolloutResult # noqa: E402 -SPACE = os.environ.get("OPENCODE_ENV_SPACE", "https://adithyask-opencode-env.hf.space") +SPACE = os.environ.get("CODING_AGENT_ENV_SPACE", "https://adithyask-coding-agent-env.hf.space") INSTRUCTION = ( "Create a single Python file named `binary_search.py` in the current " @@ -91,7 +91,7 @@ async def main() -> int: print(f"Instruction: {INSTRUCTION.splitlines()[0]} ...") print() - async with OpenCodeEnv(base_url=SPACE) as env: + async with CodingAgentEnv(base_url=SPACE) as env: await env.reset() raw = await env.call_tool( "run_rollout", @@ -101,7 +101,7 @@ async def main() -> int: instruction=INSTRUCTION, setup=[], # no setup commands verify=VERIFY, - template="opencode-rl", # prebaked E2B template + template="coding-agent-rl", # prebaked E2B template task_id="binary_search_simple", agent_timeout_s=600, ) diff --git a/tests/envs/test_opencode_env.py b/tests/envs/test_coding_agent_env.py similarity index 73% rename from tests/envs/test_opencode_env.py rename to tests/envs/test_coding_agent_env.py index 5e930b8bc..ec1f66fa5 100644 --- a/tests/envs/test_opencode_env.py +++ b/tests/envs/test_coding_agent_env.py @@ -4,7 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Smoke tests for ``opencode_env``. +"""Smoke tests for ``coding_agent_env``. The default suite runs in CI without any external dependencies (no E2B, no LLM, no network). It covers: @@ -13,7 +13,7 @@ - The endpoint catalog (`vllm` / `openai` / `hf_router`) resolves explicit + env-var + default-value precedence correctly. - Pydantic models accept their expected shapes. - - The `OpenCodeTask` coercion helper handles str / dict / `OpenCodeTask`. + - The `CodingAgentTask` coercion helper handles str / dict / `CodingAgentTask`. A second class is marked ``@pytest.mark.integration`` and exercises the deployed Space end-to-end. It only runs when ``E2B_API_KEY`` and at least @@ -45,15 +45,15 @@ def test_public_api_imports() -> None: """Top-level package re-exports the documented surface.""" - from opencode_env import ( # noqa: F401 + from coding_agent_env import ( # noqa: F401 CommandResult, E2BSandboxBackend, - OpenCodeConfig, - OpenCodeEnv, - OpenCodeSession, - OpenCodeSessionFactory, - OpenCodeState, - OpenCodeTask, + CodingAgentConfig, + CodingAgentEnv, + CodingAgentSession, + CodingAgentSessionFactory, + CodingAgentState, + CodingAgentTask, Provider, RolloutResult, RolloutTurn, @@ -64,14 +64,14 @@ def test_public_api_imports() -> None: def test_server_modules_import() -> None: """Server-side modules (FastAPI app, MCP env, catalog) import cleanly.""" - from opencode_env.server.app import app # noqa: F401 - from opencode_env.server.catalog import ( # noqa: F401 + from coding_agent_env.server.app import app # noqa: F401 + from coding_agent_env.server.catalog import ( # noqa: F401 catalog_summary, ENDPOINT_KINDS, resolve_endpoint, ) - from opencode_env.server.opencode_environment import ( # noqa: F401 - OpenCodeEnvironment, + from coding_agent_env.server.coding_environment import ( # noqa: F401 + CodingAgentEnvironment, ) @@ -81,14 +81,14 @@ def test_server_modules_import() -> None: def test_catalog_kinds() -> None: - from opencode_env.server.catalog import ENDPOINT_KINDS + from coding_agent_env.server.catalog import ENDPOINT_KINDS assert ENDPOINT_KINDS == ("vllm", "openai", "hf_router") def test_resolve_endpoint_explicit_args_win(monkeypatch: pytest.MonkeyPatch) -> None: """Explicit args beat env vars beat catalog defaults.""" - from opencode_env.server.catalog import resolve_endpoint + from coding_agent_env.server.catalog import resolve_endpoint monkeypatch.setenv("OPENAI_API_KEY", "from-env") r = resolve_endpoint( @@ -107,7 +107,7 @@ def test_resolve_endpoint_explicit_args_win(monkeypatch: pytest.MonkeyPatch) -> def test_resolve_endpoint_env_var_used_when_arg_missing( monkeypatch: pytest.MonkeyPatch, ) -> None: - from opencode_env.server.catalog import resolve_endpoint + from coding_agent_env.server.catalog import resolve_endpoint monkeypatch.setenv("OPENAI_API_KEY", "key-from-env") monkeypatch.setenv("OPENAI_MODEL", "gpt-4o") @@ -121,7 +121,7 @@ def test_resolve_endpoint_normalizes_v1_suffix( monkeypatch: pytest.MonkeyPatch, ) -> None: """Base URL gets ``/v1`` appended if missing, otherwise left alone.""" - from opencode_env.server.catalog import resolve_endpoint + from coding_agent_env.server.catalog import resolve_endpoint monkeypatch.setenv("VLLM_URL", "https://my-vllm.example/") monkeypatch.setenv("VLLM_API_KEY", "x") @@ -134,7 +134,7 @@ def test_resolve_endpoint_normalizes_v1_suffix( def test_resolve_endpoint_unknown_kind_raises() -> None: - from opencode_env.server.catalog import resolve_endpoint + from coding_agent_env.server.catalog import resolve_endpoint with pytest.raises(ValueError, match="unknown endpoint kind"): resolve_endpoint("bogus", base_url="x", api_key="y", model="z") @@ -143,7 +143,7 @@ def test_resolve_endpoint_unknown_kind_raises() -> None: def test_resolve_endpoint_missing_creds_raises( monkeypatch: pytest.MonkeyPatch, ) -> None: - from opencode_env.server.catalog import resolve_endpoint + from coding_agent_env.server.catalog import resolve_endpoint # Strip any inherited env vars. for k in ("OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_MODEL"): @@ -153,7 +153,7 @@ def test_resolve_endpoint_missing_creds_raises( def test_catalog_summary_shape() -> None: - from opencode_env.server.catalog import catalog_summary + from coding_agent_env.server.catalog import catalog_summary summary = catalog_summary() assert {entry["kind"] for entry in summary} == {"vllm", "openai", "hf_router"} @@ -166,13 +166,67 @@ def test_catalog_summary_shape() -> None: } <= entry.keys() +def test_build_agent_config_opencode() -> None: + from coding_agent_env.server.coding_environment import CodingAgentEnvironment + + env = CodingAgentEnvironment() + cfg = env._build_agent_config( + agent="opencode", + mode="transparent_proxy", + base_url="https://api.openai.com/v1", + api_key="sk-test", + model="gpt-4o-mini", + agent_timeout_s=123.0, + disable_thinking=True, + top_logprobs=7, + max_tokens_cap=2048, + ) + assert isinstance(cfg, env._CodingAgentConfig) + assert cfg.proxy_disable_thinking is True + assert cfg.proxy_top_logprobs == 7 + assert cfg.proxy_max_tokens_cap == 2048 + + +def test_build_agent_config_pi() -> None: + from coding_agent_env.server.coding_environment import CodingAgentEnvironment + + env = CodingAgentEnvironment() + cfg = env._build_agent_config( + agent="pi", + mode="black_box", + base_url="https://router.huggingface.co/v1", + api_key="hf_xxx", + model="zai-org/GLM-5.1", + agent_timeout_s=180.0, + disable_thinking=True, + top_logprobs=5, + max_tokens_cap=4096, + ) + assert cfg.provider == "huggingface" + assert cfg.thinking == "off" + assert cfg.model == "zai-org/GLM-5.1" + + cfg_proxy = env._build_agent_config( + agent="pi", + mode="transparent_proxy", + base_url="https://router.huggingface.co/v1", + api_key="hf_xxx", + model="zai-org/GLM-5.1", + agent_timeout_s=180.0, + disable_thinking=False, + top_logprobs=5, + max_tokens_cap=4096, + ) + assert cfg_proxy.provider == "openai" + + # --------------------------------------------------------------------------- # Models + task coercion # --------------------------------------------------------------------------- def test_rollout_result_serializes_round_trip() -> None: - from opencode_env import CommandResult, RolloutResult, RolloutTurn + from coding_agent_env import CommandResult, RolloutResult, RolloutTurn r = RolloutResult( task_id="t1", @@ -201,40 +255,40 @@ def test_rollout_result_serializes_round_trip() -> None: assert rebuilt.proxy_turns[0].completion_tokens == ["hi"] -def test_opencode_task_coerce_str() -> None: - from opencode_env import OpenCodeTask +def test_coding_agent_task_coerce_str() -> None: + from coding_agent_env import CodingAgentTask - t = OpenCodeTask.coerce("write fizzbuzz.py") + t = CodingAgentTask.coerce("write fizzbuzz.py") assert t.instruction == "write fizzbuzz.py" assert t.setup_shell is None assert t.upload_files == {} -def test_opencode_task_coerce_dict() -> None: - from opencode_env import OpenCodeTask +def test_coding_agent_task_coerce_dict() -> None: + from coding_agent_env import CodingAgentTask - t = OpenCodeTask.coerce({"instruction": "x", "setup_shell": "pip install pandas"}) + t = CodingAgentTask.coerce({"instruction": "x", "setup_shell": "pip install pandas"}) assert t.instruction == "x" assert t.setup_shell == "pip install pandas" -def test_opencode_task_coerce_existing_passthrough() -> None: - from opencode_env import OpenCodeTask +def test_coding_agent_task_coerce_existing_passthrough() -> None: + from coding_agent_env import CodingAgentTask - src = OpenCodeTask(instruction="y") - assert OpenCodeTask.coerce(src) is src + src = CodingAgentTask(instruction="y") + assert CodingAgentTask.coerce(src) is src -def test_opencode_task_coerce_rejects_unknown_type() -> None: - from opencode_env import OpenCodeTask +def test_coding_agent_task_coerce_rejects_unknown_type() -> None: + from coding_agent_env import CodingAgentTask with pytest.raises(TypeError, match="Cannot coerce"): - OpenCodeTask.coerce(42) # type: ignore[arg-type] + CodingAgentTask.coerce(42) # type: ignore[arg-type] def test_start_proxy_keeps_upstream_key_out_of_command() -> None: """The proxy API key must be passed via env, not shell argv.""" - from opencode_env import OpenCodeConfig, OpenCodeSessionFactory + from coding_agent_env import CodingAgentConfig, CodingAgentSessionFactory class FakeExecResult: exit_code = 0 @@ -278,13 +332,13 @@ def kill(self) -> None: secret = "sk-test '$(leak)" model = "provider/model'; touch /tmp/pwn #" - config = OpenCodeConfig( + config = CodingAgentConfig( base_url="https://example.test/v1?x='y", api_key=secret, model=model, ) sandbox = FakeSandbox() - factory = OpenCodeSessionFactory( + factory = CodingAgentSessionFactory( config=config, sandbox_backend=object(), # unused by this protected-method test mode="transparent_proxy", @@ -354,16 +408,16 @@ def test_run_rollout_e2e_via_deployed_space() -> None: import asyncio - from opencode_env import OpenCodeEnv - from opencode_env.client import _extract_text - from opencode_env.models import RolloutResult + from coding_agent_env import CodingAgentEnv + from coding_agent_env.client import _extract_text + from coding_agent_env.models import RolloutResult SPACE = os.environ.get( - "OPENCODE_ENV_SPACE", "https://adithyask-opencode-env.hf.space" + "CODING_AGENT_ENV_SPACE", "https://adithyask-coding-agent-env.hf.space" ) async def _go() -> RolloutResult: - async with OpenCodeEnv(base_url=SPACE) as env: + async with CodingAgentEnv(base_url=SPACE) as env: await env.reset() raw = await env.call_tool( "run_rollout", @@ -382,7 +436,7 @@ async def _go() -> RolloutResult: "import binary_search; " "assert binary_search.binary_search([1,2,3,4,5], 3) == 2; print('OK')\"", ], - template="opencode-rl", + template="coding-agent-rl", agent_timeout_s=600, ) return RolloutResult.model_validate_json(_extract_text(raw)) From ddf1313bd7e9f3695d08a8d079fabbeb8b8d9608 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Thu, 14 May 2026 15:06:07 +0530 Subject: [PATCH 09/79] feat: hf sandbox backend - tests --- src/openenv/core/harness/sandbox/__init__.py | 19 +- .../core/harness/sandbox/hf_backend.py | 306 ++++++++++++++++++ tests/core/test_hf_sandbox_backend.py | 221 +++++++++++++ 3 files changed, 544 insertions(+), 2 deletions(-) create mode 100644 src/openenv/core/harness/sandbox/hf_backend.py create mode 100644 tests/core/test_hf_sandbox_backend.py diff --git a/src/openenv/core/harness/sandbox/__init__.py b/src/openenv/core/harness/sandbox/__init__.py index 83d37fb48..208fe54d5 100644 --- a/src/openenv/core/harness/sandbox/__init__.py +++ b/src/openenv/core/harness/sandbox/__init__.py @@ -37,9 +37,16 @@ except ImportError: pass # e2b not installed +try: + from .hf_backend import HFBgJob, HFSandboxBackend, HFSandboxHandle # noqa: F401 + + __all__.extend(["HFBgJob", "HFSandboxBackend", "HFSandboxHandle"]) +except ImportError: + pass # hf-sandbox not installed + def create_sandbox_backend( - backend: Literal["e2b", "docker"] = "e2b", + backend: Literal["e2b", "docker", "hf"] = "e2b", **kwargs: Any, ) -> SandboxBackend: """Create a sandbox backend by name. @@ -48,6 +55,8 @@ def create_sandbox_backend( (set ``E2B_API_URL``). For ``"docker"``: local Docker, no external dependencies. + + For ``"hf"``: Hugging Face Jobs via ``hf-sandbox``. """ if backend == "e2b": from .e2b_backend import E2BSandboxBackend @@ -55,4 +64,10 @@ def create_sandbox_backend( return E2BSandboxBackend(**kwargs) elif backend == "docker": return DockerSandboxBackend(**kwargs) - raise ValueError(f"Unknown sandbox backend: {backend!r}. Use 'e2b' or 'docker'.") + elif backend == "hf": + from .hf_backend import HFSandboxBackend + + return HFSandboxBackend(**kwargs) + raise ValueError( + f"Unknown sandbox backend: {backend!r}. Use 'e2b', 'docker', or 'hf'." + ) diff --git a/src/openenv/core/harness/sandbox/hf_backend.py b/src/openenv/core/harness/sandbox/hf_backend.py new file mode 100644 index 000000000..410a8daea --- /dev/null +++ b/src/openenv/core/harness/sandbox/hf_backend.py @@ -0,0 +1,306 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Hugging Face Sandbox implementation of :class:`SandboxBackend`. + +Wraps `hf-sandbox` (https://github.com/huggingface/hf-sandbox) so OpenEnv +harnesses can use it through the same protocol. +""" + +from __future__ import annotations + +import re +import time +import uuid +from pathlib import PurePosixPath +from threading import Event +from typing import Any + +from hf_sandbox import Sandbox +from openenv.core.harness.sandbox.base import BgJob, ExecResult, SandboxHandle + +_ENV_KEY_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") + + +class HFSandboxError(RuntimeError): + """Base class for HF sandbox backend errors.""" + + +class HFSandboxCreateError(HFSandboxError): + """Raised when backend cannot create a sandbox.""" + + +class HFBgJob: + """Background process handle for :class:`HFSandboxHandle`.""" + + def __init__( + self, + sandbox: "HFSandboxHandle", + *, + pid: int, + marker_path: str, + poll_interval_s: float = 0.5, + ) -> None: + self._sandbox = sandbox + self._pid = pid + self._marker_path = marker_path + self._poll_interval_s = poll_interval_s + self._done = Event() + self._exit_code: int | None = None + + @property + def pid(self) -> int: + return self._pid + + def wait(self, timeout: float | None = None) -> int: + deadline = None if timeout is None else (time.monotonic() + timeout) + while True: + if self._done.is_set(): + return self._exit_code if self._exit_code is not None else 0 + if deadline is not None and time.monotonic() > deadline: + raise TimeoutError( + f"Background command (pid={self._pid}) " + f"did not exit within {timeout}s" + ) + + marker = self._sandbox.exec( + f"cat {_shell_quote(self._marker_path)}", + timeout=10, + ) + if marker.exit_code == 0 and marker.stdout.strip(): + self._exit_code = _parse_exit_code(marker.stdout.strip(), default=0) + self._done.set() + return self._exit_code + + alive = self._sandbox.exec(f"kill -0 {self._pid}", timeout=10) + if alive.exit_code != 0: + self._exit_code = 1 + self._done.set() + return self._exit_code + + time.sleep(self._poll_interval_s) + + def kill(self) -> None: + if self._done.is_set(): + return + try: + self._sandbox.exec(f"kill -9 {self._pid}", timeout=5) + except Exception: + pass + self._exit_code = 137 + self._done.set() + + +class HFSandboxHandle: + """Wraps a live ``hf_sandbox.Sandbox`` to satisfy :class:`SandboxHandle`.""" + + def __init__( + self, + sandbox: Any, + *, + default_envs: dict[str, str] | None = None, + ) -> None: + self._sbx = sandbox + self._default_envs = dict(default_envs or {}) + self._bg_jobs: list[HFBgJob] = [] + + @property + def sandbox_id(self) -> str: + return str(getattr(self._sbx, "job_id", "hf-sandbox")) + + @property + def raw(self) -> Any: + """Escape hatch for callers that need the underlying SDK object.""" + return self._sbx + + def exec( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + timeout: float | None = 60, + ) -> ExecResult: + merged_envs = dict(self._default_envs) + merged_envs.update(envs or {}) + shell_cmd = _with_env_prefix(cmd, merged_envs) + timeout_s = _normalize_exec_timeout(timeout) + try: + result = self._sbx.exec( + "bash", + "-lc", + shell_cmd, + workdir=cwd, + timeout=timeout_s, + ) + return ExecResult( + exit_code=int(getattr(result, "returncode", 1)), + stdout=str(getattr(result, "stdout", "") or ""), + stderr=str(getattr(result, "stderr", "") or ""), + ) + except Exception as exc: + return ExecResult(exit_code=-1, stdout="", stderr=str(exc)) + + def start_bg( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + ) -> BgJob: + marker_path = f"/tmp/.openenv_bg_{uuid.uuid4().hex[:12]}.exit" + wrapped = f"{cmd}; rc=$?; echo $rc > {_shell_quote(marker_path)}" + launch_cmd = f"nohup bash -lc {_shell_quote(wrapped)} >/dev/null 2>&1 & echo $!" + + result = self.exec(launch_cmd, envs=envs, cwd=cwd, timeout=30) + if result.exit_code != 0: + raise RuntimeError( + f"Failed to start background command: {result.stderr or result.stdout}" + ) + + pid = _parse_pid(result.stdout) + if pid is None: + raise RuntimeError(f"Could not extract PID from start_bg output: {result.stdout!r}") + + job = HFBgJob(self, pid=pid, marker_path=marker_path) + self._bg_jobs.append(job) + return job + + def write_text(self, path: str, content: str) -> None: + parent = str(PurePosixPath(path).parent) + if parent not in ("", "/"): + r = self.exec(f"mkdir -p {_shell_quote(parent)}", timeout=10) + if r.exit_code != 0: + raise RuntimeError(f"Failed to create parent directory {parent!r}: {r.stderr}") + self._sbx.write_file(path, content) + + def read_text(self, path: str) -> str: + return str(self._sbx.read_file(path, text=True)) + + def exists(self, path: str) -> bool: + r = self.exec(f"test -e {_shell_quote(path)}", timeout=10) + return r.exit_code == 0 + + def kill(self) -> None: + for job in self._bg_jobs: + try: + job.kill() + except Exception: + pass + self._bg_jobs.clear() + try: + self._sbx.terminate() + except Exception: + pass + + +class HFSandboxBackend: + """Creates HF sandboxes for harness rollouts via ``hf-sandbox``.""" + + def __init__( + self, + *, + image: str = "python:3.12", + flavor: str = "cpu-basic", + timeout: str | None = None, + forward_hf_token: bool = False, + create_retries: int = 3, + create_backoff_s: float = 2.0, + ) -> None: + self._image = image + self._flavor = flavor + self._timeout = timeout + self._forward_hf_token = forward_hf_token + self._create_retries = max(1, int(create_retries)) + self._create_backoff_s = max(0.0, float(create_backoff_s)) + + def create( + self, + *, + timeout_s: int = 900, + envs: dict[str, str] | None = None, + metadata: dict[str, str] | None = None, + ) -> SandboxHandle: + # `hf-sandbox` does not support metadata at create-time yet. + del metadata + + timeout = self._timeout or _format_timeout(timeout_s) + last_error: Exception | None = None + + for attempt in range(self._create_retries): + try: + sbx = Sandbox.create( + image=self._image, + flavor=self._flavor, + timeout=timeout, + forward_hf_token=self._forward_hf_token, + ) + return HFSandboxHandle(sbx, default_envs=envs) + except Exception as exc: # noqa: BLE001 + last_error = exc + if attempt + 1 < self._create_retries: + time.sleep(self._create_backoff_s * (2**attempt)) + + assert last_error is not None + raise HFSandboxCreateError( + f"Failed to create HF sandbox after {self._create_retries} attempts: " + f"{last_error}" + ) from last_error + + +def _with_env_prefix(cmd: str, envs: dict[str, str]) -> str: + if not envs: + return cmd + parts: list[str] = [] + for key, value in envs.items(): + if not _ENV_KEY_RE.match(key): + raise ValueError(f"Invalid environment variable name: {key!r}") + parts.append(f"export {key}={_shell_quote(str(value))};") + return " ".join(parts) + f" {cmd}" + + +def _normalize_exec_timeout(timeout: float | None) -> int: + if timeout is None: + return 24 * 60 * 60 + return max(1, int(timeout)) + + +def _format_timeout(timeout_s: int) -> str: + timeout_s = max(1, int(timeout_s)) + if timeout_s % 3600 == 0: + return f"{timeout_s // 3600}h" + if timeout_s % 60 == 0: + return f"{timeout_s // 60}m" + return f"{timeout_s}s" + + +def _parse_pid(stdout: str) -> int | None: + for line in reversed(stdout.strip().splitlines()): + raw = line.strip() + if raw.isdigit(): + return int(raw) + return None + + +def _parse_exit_code(raw: str, *, default: int) -> int: + try: + return int(raw.splitlines()[-1].strip()) + except Exception: + return default + + +def _shell_quote(s: str) -> str: + """Single-quote a string for shell, escaping embedded single quotes.""" + return "'" + s.replace("'", "'\\''") + "'" + + +__all__ = [ + "HFBgJob", + "HFSandboxBackend", + "HFSandboxCreateError", + "HFSandboxError", + "HFSandboxHandle", +] diff --git a/tests/core/test_hf_sandbox_backend.py b/tests/core/test_hf_sandbox_backend.py new file mode 100644 index 000000000..d301b2b2b --- /dev/null +++ b/tests/core/test_hf_sandbox_backend.py @@ -0,0 +1,221 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Unit tests for the HF sandbox backend. + +These tests mock ``hf-sandbox`` so they run without network or HF credentials. +""" + +from __future__ import annotations + +import importlib +import re +import subprocess +import sys +import types +from dataclasses import dataclass, field + +import pytest + + +@dataclass +class _FakeSandbox: + job_id: str + files: dict[str, str] = field(default_factory=dict) + marker_files: dict[str, str] = field(default_factory=dict) + bg_jobs: dict[int, dict] = field(default_factory=dict) + next_pid: int = 1000 + terminated: bool = False + + def exec( + self, + *cmd: str, + workdir: str | None = None, + stdin: str | None = None, + timeout: int = 600, + ) -> subprocess.CompletedProcess: + del workdir, stdin, timeout + if len(cmd) < 3: + return subprocess.CompletedProcess(cmd, 1, "", "invalid command") + script = cmd[2] + + if "ok_cmd" in script: + return subprocess.CompletedProcess(cmd, 0, "ok\n", "") + if "fail_cmd" in script: + return subprocess.CompletedProcess(cmd, 42, "", "failed") + if "timeout_cmd" in script: + return subprocess.CompletedProcess(cmd, -1, "", "timeout") + + if "mkdir -p" in script: + return subprocess.CompletedProcess(cmd, 0, "", "") + + if "test -e " in script: + match = re.search(r"test -e '([^']+)'", script) + assert match is not None + path = match.group(1) + exists = path in self.files or path in self.marker_files + return subprocess.CompletedProcess(cmd, 0 if exists else 1, "", "") + + if "cat '/tmp/.openenv_bg_" in script: + match = re.search(r"cat '([^']+)'", script) + assert match is not None + marker = match.group(1) + if marker in self.marker_files: + return subprocess.CompletedProcess( + cmd, + 0, + f"{self.marker_files[marker]}\n", + "", + ) + return subprocess.CompletedProcess(cmd, 1, "", "missing") + + if script.strip().startswith("kill -0 "): + pid = int(script.strip().split()[2]) + alive = self.bg_jobs.get(pid, {}).get("alive", False) + return subprocess.CompletedProcess(cmd, 0 if alive else 1, "", "") + + if script.strip().startswith("kill -9 "): + pid = int(script.strip().split()[2]) + if pid in self.bg_jobs: + self.bg_jobs[pid]["alive"] = False + marker = self.bg_jobs[pid]["marker"] + self.marker_files[marker] = "137" + return subprocess.CompletedProcess(cmd, 0, "", "") + + if "echo $!" in script: + marker_match = re.search(r"(/tmp/\.openenv_bg_[A-Za-z0-9]+\.exit)", script) + assert marker_match is not None + marker = marker_match.group(1) + pid = self.next_pid + self.next_pid += 1 + long_running = "sleep 300" in script + self.bg_jobs[pid] = { + "marker": marker, + "alive": long_running, + } + if not long_running: + self.marker_files[marker] = "0" + return subprocess.CompletedProcess(cmd, 0, f"{pid}\n", "") + + return subprocess.CompletedProcess(cmd, 0, "", "") + + def write_file( + self, + path: str, + content: str | bytes | bytearray | memoryview, + ) -> None: + if isinstance(content, str): + normalized = content + else: + normalized = bytes(content).decode("utf-8", "replace") + self.files[path] = normalized + + def read_file(self, path: str, text: bool = True) -> str | bytes: + if path not in self.files: + raise FileNotFoundError(path) + return self.files[path] if text else self.files[path].encode() + + def terminate(self) -> None: + self.terminated = True + + +class _FakeSandboxAPI: + calls: list[dict] = [] + + @classmethod + def create( + cls, + image: str, + flavor: str, + timeout: str, + forward_hf_token: bool, + ) -> _FakeSandbox: + cls.calls.append( + { + "image": image, + "flavor": flavor, + "timeout": timeout, + "forward_hf_token": forward_hf_token, + } + ) + return _FakeSandbox(job_id="job-123") + + +def _install_fake_hf_sandbox(monkeypatch) -> None: + fake_module = types.ModuleType("hf_sandbox") + setattr(fake_module, "Sandbox", _FakeSandboxAPI) + monkeypatch.setitem(sys.modules, "hf_sandbox", fake_module) + + +class TestHFSandboxBackend: + def test_exported_from_package(self, monkeypatch): + _install_fake_hf_sandbox(monkeypatch) + + import openenv.core.harness.sandbox as sandbox_pkg + + importlib.reload(sandbox_pkg) + assert hasattr(sandbox_pkg, "HFSandboxBackend") + assert hasattr(sandbox_pkg, "HFSandboxHandle") + assert hasattr(sandbox_pkg, "HFBgJob") + + def test_create_exec_write_read_exists_bg_and_kill(self, monkeypatch): + import openenv.core.harness.sandbox.hf_backend as hf_backend + + _install_fake_hf_sandbox(monkeypatch) + importlib.reload(hf_backend) + + _FakeSandboxAPI.calls.clear() + monkeypatch.setattr(hf_backend, "Sandbox", _FakeSandboxAPI) + + backend = hf_backend.HFSandboxBackend( + image="python:3.12", + flavor="cpu-basic", + forward_hf_token=True, + ) + sandbox = backend.create(timeout_s=120, envs={"GLOBAL_ENV": "on"}) + + assert sandbox.sandbox_id == "job-123" + assert _FakeSandboxAPI.calls[-1]["timeout"] == "2m" + + ok = sandbox.exec("ok_cmd") + assert ok.exit_code == 0 + + failed = sandbox.exec("fail_cmd") + assert failed.exit_code == 42 + + timed = sandbox.exec("timeout_cmd") + assert timed.exit_code == -1 + + sandbox.write_text("/tmp/hello.txt", "hello") + assert sandbox.exists("/tmp/hello.txt") + assert sandbox.read_text("/tmp/hello.txt") == "hello" + + short_job = sandbox.start_bg("echo done > /tmp/bg.txt") + assert short_job.wait(timeout=2) == 0 + + long_job = sandbox.start_bg("sleep 300") + with pytest.raises(TimeoutError): + long_job.wait(timeout=0.1) + long_job.kill() + assert isinstance(long_job.wait(timeout=2), int) + + sandbox.kill() + raw = getattr(sandbox, "raw", None) + assert raw is not None + assert raw.terminated is True + + def test_factory_creates_hf_backend(self, monkeypatch): + _install_fake_hf_sandbox(monkeypatch) + + import openenv.core.harness.sandbox.hf_backend as hf_backend + import openenv.core.harness.sandbox as sandbox_pkg + + importlib.reload(hf_backend) + importlib.reload(sandbox_pkg) + + monkeypatch.setattr(hf_backend, "Sandbox", _FakeSandboxAPI) + backend = sandbox_pkg.create_sandbox_backend("hf", image="python:3.12") + assert isinstance(backend, hf_backend.HFSandboxBackend) From 9d856401edd6d2e23bebec130716d9674a5eba45 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 15 May 2026 14:23:56 +0530 Subject: [PATCH 10/79] chore: ruff + usort format pass --- envs/agent_world_model_env/server/web_ui.py | 4 +- envs/chat_env/models.py | 4 +- envs/chat_env/server/chat_environment.py | 4 +- envs/coding_agent_env/client.py | 1 - .../sandbox/build_template.py | 2 +- .../server/coding_environment.py | 5 +- envs/coding_agent_env/server/gradio_ui.py | 1 - .../server/coding_tools_env_environment.py | 65 ++++++++++--- envs/coding_tools_env/server/e2b_sandbox.py | 23 ++++- envs/coding_tools_env/server/gradio_ui.py | 94 +++++++++++++------ .../jupyter_env/server/jupyter_environment.py | 10 +- envs/repl_env/server/repl_environment.py | 4 +- .../server/terminus_env_environment.py | 10 +- envs/textarena_env/server/gradio_ui.py | 8 +- .../core/harness/sandbox/hf_backend.py | 8 +- tests/core/test_hf_sandbox_backend.py | 2 +- tests/envs/test_coding_agent_env.py | 8 +- 17 files changed, 178 insertions(+), 75 deletions(-) diff --git a/envs/agent_world_model_env/server/web_ui.py b/envs/agent_world_model_env/server/web_ui.py index 84b10c6b2..09b445d3f 100644 --- a/envs/agent_world_model_env/server/web_ui.py +++ b/envs/agent_world_model_env/server/web_ui.py @@ -21,9 +21,7 @@ # Keep in sync with DEFAULT_REWARD_CONFIG in config.py. -_DEFAULT_REWARD_JSON = json.dumps( - DEFAULT_REWARD_CONFIG, indent=2 -) +_DEFAULT_REWARD_JSON = json.dumps(DEFAULT_REWARD_CONFIG, indent=2) def _format_obs_md(payload: dict | None) -> str: diff --git a/envs/chat_env/models.py b/envs/chat_env/models.py index 8bc10f09e..da994cbe3 100644 --- a/envs/chat_env/models.py +++ b/envs/chat_env/models.py @@ -55,7 +55,9 @@ class ChatState(State): """State of the ChatEnvironment containing message history.""" history_messages: list[Message] = Field(default_factory=list) - history_tokens: list[list[int]] = Field(default_factory=list) # Same len as messages + history_tokens: list[list[int]] = Field( + default_factory=list + ) # Same len as messages class ChatObservation(Observation): diff --git a/envs/chat_env/server/chat_environment.py b/envs/chat_env/server/chat_environment.py index 90b2d01f0..f66f3e790 100644 --- a/envs/chat_env/server/chat_environment.py +++ b/envs/chat_env/server/chat_environment.py @@ -90,7 +90,9 @@ def _coerce_tokens(self, tokens) -> list[int]: def _tokenize_conversation(self, conversation: list[Message]) -> list[int]: """Tokenize a conversation with a chat-template fallback for base tokenizers.""" try: - tokens = self.tokenizer.apply_chat_template(conversation=conversation, tokenize=True) + tokens = self.tokenizer.apply_chat_template( + conversation=conversation, tokenize=True + ) except Exception: # Some tokenizers (e.g. gpt2) do not define `chat_template`. fallback_text = "".join( diff --git a/envs/coding_agent_env/client.py b/envs/coding_agent_env/client.py index 8c512090d..7e2a21696 100644 --- a/envs/coding_agent_env/client.py +++ b/envs/coding_agent_env/client.py @@ -169,4 +169,3 @@ def _extract_text(result: Any) -> str: return text return str(result) - diff --git a/envs/coding_agent_env/sandbox/build_template.py b/envs/coding_agent_env/sandbox/build_template.py index e22b30185..e1fdac50a 100644 --- a/envs/coding_agent_env/sandbox/build_template.py +++ b/envs/coding_agent_env/sandbox/build_template.py @@ -114,7 +114,7 @@ def main(argv: list[str] | None = None) -> int: p.add_argument( "--name", default="coding-agent-rl", - help="Template name (default: coding-agent-rl)." + help="Template name (default: coding-agent-rl).", ) p.add_argument( "--skip-cache", diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index 3f8eabd13..e389eb759 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -480,7 +480,9 @@ def _build_agent_config( ) provider = ( - "openai" if mode == "transparent_proxy" else self._infer_pi_provider(base_url) + "openai" + if mode == "transparent_proxy" + else self._infer_pi_provider(base_url) ) return _GenericAgentConfig( base_url=base_url.rstrip("/"), @@ -654,4 +656,3 @@ def _safe_read(sandbox: Any, path: str) -> str: return sandbox.read_text(path) or "" except Exception: return "" - diff --git a/envs/coding_agent_env/server/gradio_ui.py b/envs/coding_agent_env/server/gradio_ui.py index 5497ef0f2..ef3f94aeb 100644 --- a/envs/coding_agent_env/server/gradio_ui.py +++ b/envs/coding_agent_env/server/gradio_ui.py @@ -611,4 +611,3 @@ def apply_preset(name: str) -> tuple[str, str, str]: ) return app - diff --git a/envs/coding_tools_env/server/coding_tools_env_environment.py b/envs/coding_tools_env/server/coding_tools_env_environment.py index 615e7770f..d0ef86675 100644 --- a/envs/coding_tools_env/server/coding_tools_env_environment.py +++ b/envs/coding_tools_env/server/coding_tools_env_environment.py @@ -45,16 +45,28 @@ def bash(command: str, timeout: float | None = 30) -> str: return "Error: environment not reset. Call reset() first." timeout_value = 30 if timeout is None else float(timeout) result = self._sandbox.run_shell(command, timeout_s=timeout_value) - self._record("bash", result.ok, result.output, result.error, result.metadata) - return result.output if result.ok else f"ERROR: {result.error}\n{result.output}".strip() + self._record( + "bash", result.ok, result.output, result.error, result.metadata + ) + return ( + result.output + if result.ok + else f"ERROR: {result.error}\n{result.output}".strip() + ) @mcp.tool - def read(file_path: str, offset: int | None = None, limit: int | None = None) -> str: + def read( + file_path: str, offset: int | None = None, limit: int | None = None + ) -> str: """Read file contents using computer instance.""" if not self._sandbox: return "Error: environment not reset. Call reset() first." - result = self._sandbox.read_file(file_path=file_path, offset=offset, limit=limit) - self._record("read", result.ok, result.output, result.error, result.metadata) + result = self._sandbox.read_file( + file_path=file_path, offset=offset, limit=limit + ) + self._record( + "read", result.ok, result.output, result.error, result.metadata + ) return result.output if result.ok else f"ERROR: {result.error}" @mcp.tool @@ -63,7 +75,9 @@ def write(file_path: str, content: str) -> str: if not self._sandbox: return "Error: environment not reset. Call reset() first." result = self._sandbox.write_file(file_path=file_path, content=content) - self._record("write", result.ok, result.output, result.error, result.metadata) + self._record( + "write", result.ok, result.output, result.error, result.metadata + ) return result.output if result.ok else f"ERROR: {result.error}" @mcp.tool @@ -88,10 +102,14 @@ def edit( updated = original.replace(old_string, new_string) else: updated = original.replace(old_string, new_string, 1) - write_result = self._sandbox.write_file(file_path=file_path, content=updated) + write_result = self._sandbox.write_file( + file_path=file_path, content=updated + ) ok = write_result.ok msg = "edit ok" if ok else "" - self._record("edit", ok, msg, write_result.error, {"replace_all": replace_all}) + self._record( + "edit", ok, msg, write_result.error, {"replace_all": replace_all} + ) return msg if ok else f"ERROR: {write_result.error}" @mcp.tool @@ -129,7 +147,11 @@ def multi_edit(file_path: str, edits: list[dict[str, Any]]) -> str: write_result.error, {"applied": applied}, ) - return f"applied {applied} edits" if write_result.ok else f"ERROR: {write_result.error}" + return ( + f"applied {applied} edits" + if write_result.ok + else f"ERROR: {write_result.error}" + ) @mcp.tool def glob(pattern: str, path: str | None = None) -> str: @@ -137,17 +159,27 @@ def glob(pattern: str, path: str | None = None) -> str: if not self._sandbox: return "Error: environment not reset. Call reset() first." result = self._sandbox.glob_files(pattern=pattern, path=path) - self._record("glob", result.ok, result.output, result.error, result.metadata) + self._record( + "glob", result.ok, result.output, result.error, result.metadata + ) return result.output if result.ok else f"ERROR: {result.error}" @mcp.tool - def grep(pattern: str, path: str | None = None, include: str | None = None) -> str: + def grep( + pattern: str, path: str | None = None, include: str | None = None + ) -> str: """Search for patterns in files.""" if not self._sandbox: return "Error: environment not reset. Call reset() first." result = self._sandbox.grep(pattern=pattern, path=path, include=include) - self._record("grep", result.ok, result.output, result.error, result.metadata) - return result.output if result.ok else f"ERROR: {result.error}\n{result.output}".strip() + self._record( + "grep", result.ok, result.output, result.error, result.metadata + ) + return ( + result.output + if result.ok + else f"ERROR: {result.error}\n{result.output}".strip() + ) @mcp.tool def ls(path: str = ".", ignore: list[str] | None = None) -> str: @@ -177,7 +209,9 @@ def todo_write(todos: list[dict[str, Any]]) -> str: self._record("todo_write", False, "", msg, None) return msg self._state.todos = validated - self._record("todo_write", True, f"stored {len(validated)} todos", None, None) + self._record( + "todo_write", True, f"stored {len(validated)} todos", None, None + ) return f"stored {len(validated)} todos" @mcp.tool @@ -281,7 +315,8 @@ def reset( "sandbox_id": self._state.sandbox_id, "message": "Setup command failed.", "setup_results": [ - entry.model_dump() for entry in self._state.setup_results + entry.model_dump() + for entry in self._state.setup_results ], }, ) diff --git a/envs/coding_tools_env/server/e2b_sandbox.py b/envs/coding_tools_env/server/e2b_sandbox.py index 5833c7ecb..d6f77373b 100644 --- a/envs/coding_tools_env/server/e2b_sandbox.py +++ b/envs/coding_tools_env/server/e2b_sandbox.py @@ -94,7 +94,11 @@ def read_file( def write_file(self, file_path: str, content: str) -> ToolResult: try: self._sbx.files.write(file_path, content.encode("utf-8")) - return ToolResult(ok=True, output="write ok", metadata={"bytes": len(content.encode("utf-8"))}) + return ToolResult( + ok=True, + output="write ok", + metadata={"bytes": len(content.encode("utf-8"))}, + ) except Exception as exc: return ToolResult(ok=False, error=f"write failed: {exc}") @@ -111,7 +115,9 @@ def glob_files(self, pattern: str, path: str | None = None) -> ToolResult: if result is None: return ToolResult(ok=False, error=_format_error(execution)) matches = result.get("matches", []) - return ToolResult(ok=True, output="\n".join(matches), metadata={"matches": matches}) + return ToolResult( + ok=True, output="\n".join(matches), metadata={"matches": matches} + ) def list_dir(self, path: str = ".", ignore: list[str] | None = None) -> ToolResult: ignore = ignore or [] @@ -137,10 +143,15 @@ def list_dir(self, path: str = ".", ignore: list[str] | None = None) -> ToolResu if not result.get("ok", False): return ToolResult(ok=False, error=str(result.get("error", "ls failed"))) items = result.get("items", []) - lines = [f"{'[dir]' if item['is_dir'] else '[file]'} {item['name']}" for item in items] + lines = [ + f"{'[dir]' if item['is_dir'] else '[file]'} {item['name']}" + for item in items + ] return ToolResult(ok=True, output="\n".join(lines), metadata={"items": items}) - def grep(self, pattern: str, path: str | None = None, include: str | None = None) -> ToolResult: + def grep( + self, pattern: str, path: str | None = None, include: str | None = None + ) -> ToolResult: root = path or "." code = ( "from pathlib import Path\n" @@ -173,7 +184,9 @@ def grep(self, pattern: str, path: str | None = None, include: str | None = None if not result.get("ok", False): return ToolResult(ok=False, error=str(result.get("error", "grep failed"))) matches = result.get("matches", []) - return ToolResult(ok=True, output="\n".join(matches), metadata={"matches": matches}) + return ToolResult( + ok=True, output="\n".join(matches), metadata={"matches": matches} + ) def kill(self) -> None: try: diff --git a/envs/coding_tools_env/server/gradio_ui.py b/envs/coding_tools_env/server/gradio_ui.py index c0d670a99..1f3845141 100644 --- a/envs/coding_tools_env/server/gradio_ui.py +++ b/envs/coding_tools_env/server/gradio_ui.py @@ -105,7 +105,9 @@ def _extract_tool_error(result: dict[str, Any]) -> bool: def _format_status(state: dict[str, Any]) -> str: if not state: - return "**No active session.** Configure setup/verify and click *Reset sandbox*." + return ( + "**No active session.** Configure setup/verify and click *Reset sandbox*." + ) sandbox_id = state.get("sandbox_id") or "—" step_count = state.get("step_count", 0) submitted = state.get("submitted", False) @@ -227,9 +229,9 @@ def state_payload() -> dict[str, Any]: label="edits (JSON array)", language="json", value=( - '[\n' + "[\n" ' {"old_string": "TODO", "new_string": "DONE", "replace_all": false}\n' - ']' + "]" ), lines=8, ) @@ -260,10 +262,10 @@ def state_payload() -> dict[str, Any]: label="todos (JSON array)", language="json", value=( - '[\n' + "[\n" ' {"id":"1","content":"Inspect files",' '"status":"in_progress","priority":"high"}\n' - ']' + "]" ), lines=8, ) @@ -337,23 +339,33 @@ def on_tool_change(tool: str): return [help_md, *updates] tool_dropdown.change( - on_tool_change, inputs=[tool_dropdown], outputs=[tool_help, *group_components] + on_tool_change, + inputs=[tool_dropdown], + outputs=[tool_help, *group_components], ) # ───────── Result rendering helper ───────── - def render_result(tool: str, raw: dict[str, Any]) -> tuple[str, str, str, str, str, list[list[str]]]: + def render_result( + tool: str, raw: dict[str, Any] + ) -> tuple[str, str, str, str, str, list[list[str]]]: text = _extract_tool_text(raw) - is_error = _extract_tool_error(raw) or text.startswith("ERROR:") or text.startswith("Error:") + is_error = ( + _extract_tool_error(raw) + or text.startswith("ERROR:") + or text.startswith("Error:") + ) badge = "❌ error" if is_error else "✅ ok" status_line = f"**{tool}** — {badge}" state = state_payload() return ( - status_line, # output_status - text, # output_view - json.dumps(raw, indent=2), # raw_response - _format_status(state), # state_summary (top + summary panel — same content) + status_line, # output_status + text, # output_view + json.dumps(raw, indent=2), # raw_response + _format_status( + state + ), # state_summary (top + summary panel — same content) json.dumps(state, indent=2, default=str), # state_json - _format_history(state), # history_table + _format_history(state), # history_table ) # ───────── Session handlers ───────── @@ -398,21 +410,33 @@ async def on_close(): async def on_run( tool: str, # bash - bash_command: str, bash_timeout: float, + bash_command: str, + bash_timeout: float, # read - read_path: str, read_offset: float | None, read_limit: float | None, + read_path: str, + read_offset: float | None, + read_limit: float | None, # write - write_path: str, write_content: str, + write_path: str, + write_content: str, # edit - edit_path: str, edit_old: str, edit_new: str, edit_replace_all: bool, + edit_path: str, + edit_old: str, + edit_new: str, + edit_replace_all: bool, # multi_edit - multi_edit_path: str, multi_edit_json: str, + multi_edit_path: str, + multi_edit_json: str, # glob - glob_pattern: str, glob_path: str, + glob_pattern: str, + glob_path: str, # grep - grep_pattern: str, grep_path: str, grep_include: str, + grep_pattern: str, + grep_path: str, + grep_include: str, # ls - ls_path: str, ls_ignore: str, + ls_path: str, + ls_ignore: str, # todo_write todo_json: str, ): @@ -493,14 +517,26 @@ async def on_run( # ───────── Wire up events ───────── all_inputs = [ tool_dropdown, - bash_command, bash_timeout, - read_path, read_offset, read_limit, - write_path, write_content, - edit_path, edit_old, edit_new, edit_replace_all, - multi_edit_path, multi_edit_json, - glob_pattern, glob_path, - grep_pattern, grep_path, grep_include, - ls_path, ls_ignore, + bash_command, + bash_timeout, + read_path, + read_offset, + read_limit, + write_path, + write_content, + edit_path, + edit_old, + edit_new, + edit_replace_all, + multi_edit_path, + multi_edit_json, + glob_pattern, + glob_path, + grep_pattern, + grep_path, + grep_include, + ls_path, + ls_ignore, todo_json, ] all_outputs = [ diff --git a/envs/jupyter_env/server/jupyter_environment.py b/envs/jupyter_env/server/jupyter_environment.py index bc622ae22..b7902e5d2 100644 --- a/envs/jupyter_env/server/jupyter_environment.py +++ b/envs/jupyter_env/server/jupyter_environment.py @@ -348,7 +348,10 @@ def step( ) -> Observation: self._state.step_count += 1 obs = super().step(action, timeout_s=timeout_s, **kwargs) - if self._state.submitted_answer is not None and self._state.last_reward is not None: + if ( + self._state.submitted_answer is not None + and self._state.last_reward is not None + ): obs.done = True obs.reward = self._state.last_reward return obs @@ -361,7 +364,10 @@ async def step_async( ) -> Observation: self._state.step_count += 1 obs = await super().step_async(action, timeout_s=timeout_s, **kwargs) - if self._state.submitted_answer is not None and self._state.last_reward is not None: + if ( + self._state.submitted_answer is not None + and self._state.last_reward is not None + ): obs.done = True obs.reward = self._state.last_reward return obs diff --git a/envs/repl_env/server/repl_environment.py b/envs/repl_env/server/repl_environment.py index f2e6f5d98..13a759c29 100644 --- a/envs/repl_env/server/repl_environment.py +++ b/envs/repl_env/server/repl_environment.py @@ -272,9 +272,7 @@ def reset( # reset() are treated as equal and don't trigger a redundant rebuild. resolved_model = self._resolve_model(llm_model) has_runtime_llm = self._runtime_controller is not None - model_changed = ( - has_runtime_llm and resolved_model != self._current_llm_model - ) + model_changed = has_runtime_llm and resolved_model != self._current_llm_model token_provided = hf_token is not None if not self.llm_query_fn or model_changed or token_provided: effective_token = ( diff --git a/envs/terminus_env/server/terminus_env_environment.py b/envs/terminus_env/server/terminus_env_environment.py index c6f9e1c02..03de18baa 100644 --- a/envs/terminus_env/server/terminus_env_environment.py +++ b/envs/terminus_env/server/terminus_env_environment.py @@ -183,7 +183,10 @@ def step( ) -> Observation: self._state.step_count += 1 obs = super().step(action, timeout_s=timeout_s, **kwargs) - if self._state.submitted_answer is not None and self._state.last_reward is not None: + if ( + self._state.submitted_answer is not None + and self._state.last_reward is not None + ): obs.done = True obs.reward = self._state.last_reward return obs @@ -196,7 +199,10 @@ async def step_async( ) -> Observation: self._state.step_count += 1 obs = await super().step_async(action, timeout_s=timeout_s, **kwargs) - if self._state.submitted_answer is not None and self._state.last_reward is not None: + if ( + self._state.submitted_answer is not None + and self._state.last_reward is not None + ): obs.done = True obs.reward = self._state.last_reward return obs diff --git a/envs/textarena_env/server/gradio_ui.py b/envs/textarena_env/server/gradio_ui.py index 45728fc00..c9bb88cae 100644 --- a/envs/textarena_env/server/gradio_ui.py +++ b/envs/textarena_env/server/gradio_ui.py @@ -71,7 +71,9 @@ def _sudoku_demo_html() -> str: for col in range(9): value = givens.get((row, col), "") border_right = "3px solid #0f172a" if col in {2, 5} else "1px solid #94a3b8" - border_bottom = "3px solid #0f172a" if row in {2, 5} else "1px solid #94a3b8" + border_bottom = ( + "3px solid #0f172a" if row in {2, 5} else "1px solid #94a3b8" + ) background = "#e2e8f0" if value else "#ffffff" cells.append( f""" @@ -82,7 +84,7 @@ def _sudoku_demo_html() -> str: align-items: center; justify-content: center; font-size: 1.1rem; - font-weight: {'700' if value else '400'}; + font-weight: {"700" if value else "400"}; color: #0f172a; background: {background}; border-right: {border_right}; @@ -105,7 +107,7 @@ def _sudoku_demo_html() -> str: border: 3px solid #0f172a; background: #ffffff; "> - {''.join(cells)} + {"".join(cells)}

Use the Playground tab to reset the game and submit moves in the diff --git a/src/openenv/core/harness/sandbox/hf_backend.py b/src/openenv/core/harness/sandbox/hf_backend.py index 410a8daea..bb41356e2 100644 --- a/src/openenv/core/harness/sandbox/hf_backend.py +++ b/src/openenv/core/harness/sandbox/hf_backend.py @@ -163,7 +163,9 @@ def start_bg( pid = _parse_pid(result.stdout) if pid is None: - raise RuntimeError(f"Could not extract PID from start_bg output: {result.stdout!r}") + raise RuntimeError( + f"Could not extract PID from start_bg output: {result.stdout!r}" + ) job = HFBgJob(self, pid=pid, marker_path=marker_path) self._bg_jobs.append(job) @@ -174,7 +176,9 @@ def write_text(self, path: str, content: str) -> None: if parent not in ("", "/"): r = self.exec(f"mkdir -p {_shell_quote(parent)}", timeout=10) if r.exit_code != 0: - raise RuntimeError(f"Failed to create parent directory {parent!r}: {r.stderr}") + raise RuntimeError( + f"Failed to create parent directory {parent!r}: {r.stderr}" + ) self._sbx.write_file(path, content) def read_text(self, path: str) -> str: diff --git a/tests/core/test_hf_sandbox_backend.py b/tests/core/test_hf_sandbox_backend.py index d301b2b2b..cd235c748 100644 --- a/tests/core/test_hf_sandbox_backend.py +++ b/tests/core/test_hf_sandbox_backend.py @@ -210,8 +210,8 @@ def test_create_exec_write_read_exists_bg_and_kill(self, monkeypatch): def test_factory_creates_hf_backend(self, monkeypatch): _install_fake_hf_sandbox(monkeypatch) - import openenv.core.harness.sandbox.hf_backend as hf_backend import openenv.core.harness.sandbox as sandbox_pkg + import openenv.core.harness.sandbox.hf_backend as hf_backend importlib.reload(hf_backend) importlib.reload(sandbox_pkg) diff --git a/tests/envs/test_coding_agent_env.py b/tests/envs/test_coding_agent_env.py index ec1f66fa5..3a89a3ce6 100644 --- a/tests/envs/test_coding_agent_env.py +++ b/tests/envs/test_coding_agent_env.py @@ -46,14 +46,14 @@ def test_public_api_imports() -> None: """Top-level package re-exports the documented surface.""" from coding_agent_env import ( # noqa: F401 - CommandResult, - E2BSandboxBackend, CodingAgentConfig, CodingAgentEnv, CodingAgentSession, CodingAgentSessionFactory, CodingAgentState, CodingAgentTask, + CommandResult, + E2BSandboxBackend, Provider, RolloutResult, RolloutTurn, @@ -267,7 +267,9 @@ def test_coding_agent_task_coerce_str() -> None: def test_coding_agent_task_coerce_dict() -> None: from coding_agent_env import CodingAgentTask - t = CodingAgentTask.coerce({"instruction": "x", "setup_shell": "pip install pandas"}) + t = CodingAgentTask.coerce( + {"instruction": "x", "setup_shell": "pip install pandas"} + ) assert t.instruction == "x" assert t.setup_shell == "pip install pandas" From 2f9435cb5d8936567cad5cdc272a12cb16668963 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 15 May 2026 23:35:31 +0530 Subject: [PATCH 11/79] refactor: remove transparent_proxy mode and in-sandbox interception proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the transparent proxy was a passive forwarder that captured logprobs by injecting logprobs=true into upstream requests. It is replaced by the interception_gate mode where the trainer owns the forward pass entirely — no proxy needed inside the sandbox. --- envs/coding_agent_env/config.py | 13 - .../sandbox/build_template.py | 73 +- src/openenv/core/harness/agents/base.py | 3 - src/openenv/core/harness/agents/opencode.py | 1 - src/openenv/core/harness/agents/pi.py | 1 - .../core/harness/sandbox/interception.py | 660 ------------------ 6 files changed, 3 insertions(+), 748 deletions(-) delete mode 100644 src/openenv/core/harness/sandbox/interception.py diff --git a/envs/coding_agent_env/config.py b/envs/coding_agent_env/config.py index 2eac8d16f..b3243253e 100644 --- a/envs/coding_agent_env/config.py +++ b/envs/coding_agent_env/config.py @@ -51,19 +51,6 @@ class CodingAgentConfig(BaseModel): # ``/home/user``. Override when using a root-privileged backend (Docker). sandbox_home: str = "/home/user" - # --- Transparent-proxy tuning -------------------------------------------- - # Cap ``max_tokens`` / ``max_completion_tokens`` on forwarded requests. - # OpenCode defaults to a very large number (~32000) which exceeds some - # provider limits (e.g. gpt-4o-mini = 16384). Only used in - # ``mode="transparent_proxy"``. ``None`` disables the cap. - proxy_max_tokens_cap: int | None = 16384 - # Per-turn top-k logprobs the proxy requests from the upstream. - proxy_top_logprobs: int = 5 - # Disable reasoning/thinking mode for Qwen3 / Qwen3.5 models. Proxy sets - # ``extra_body.chat_template_kwargs.enable_thinking=false`` on forwarded - # requests. Ignored by providers that don't support the field. - proxy_disable_thinking: bool = False - _PROVIDER_NPM = { "openai_compatible": "@ai-sdk/openai-compatible", diff --git a/envs/coding_agent_env/sandbox/build_template.py b/envs/coding_agent_env/sandbox/build_template.py index e1fdac50a..01978b520 100644 --- a/envs/coding_agent_env/sandbox/build_template.py +++ b/envs/coding_agent_env/sandbox/build_template.py @@ -4,35 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Build a pre-baked E2B template with opencode + proxy deps already installed. - -Run-time per rollout drops from ~3 min (cold install) to ~30s once the -template is built, because we skip: - - - ``curl https://opencode.ai/install | bash`` (~30-90s) - - ``pip install fastapi uvicorn httpx`` (~30-60s) - - directory layout setup - - copying the proxy source - -The template ships: - - - opencode CLI at ``/home/user/.opencode/bin/opencode`` - - Python deps for the in-sandbox proxy - - The proxy source at ``/home/user/proxy/interception.py`` - - Pre-created dirs: ``~/.config/opencode``, ``~/logs/{agent,verifier}``, - ``~/task``, ``~/workdir``, ``~/proxy`` - - Default workdir: ``/home/user/workdir`` - -Usage:: - - .venv/bin/python envs/coding_agent_env/sandbox/build_template.py - # → builds (or rebuilds) ``coding-agent-rl`` template, prints template id - -Then rollout tests can use it via ``--template coding-agent-rl``. - -Requires ``E2B_API_KEY`` in the environment. First build is ~3-8 min; -subsequent builds reuse the cache and can finish in <60s. -""" +"""Build a pre-baked E2B template with opencode already installed.""" from __future__ import annotations @@ -43,11 +15,7 @@ from e2b import default_build_logger, Template - _REPO_ROOT = Path(__file__).resolve().parents[3] -_PROXY_SOURCE = ( - _REPO_ROOT / "src" / "openenv" / "core" / "harness" / "sandbox" / "interception.py" -) def _load_env(path: Path) -> None: @@ -65,25 +33,9 @@ def _load_env(path: Path) -> None: def build_template(name: str, *, skip_cache: bool = False) -> str: - if not _PROXY_SOURCE.exists(): - raise RuntimeError(f"proxy source missing at {_PROXY_SOURCE}") - - # Template.copy() resolves relative paths against the caller's source - # file directory. This script lives next to ``interception.py`` so the - # bare filename works. - - # Stage 1 (root): system-wide pip deps for the proxy. - # Stage 2 (user): opencode install + dir layout + proxy copy. template = ( Template() .from_python_image("3.12") - .pip_install( - [ - "fastapi>=0.104", - "uvicorn[standard]>=0.24", - "httpx>=0.27", - ] - ) .set_user("user") .run_cmd("curl -fsSL https://opencode.ai/install | bash") .run_cmd("/home/user/.opencode/bin/opencode --version") @@ -92,13 +44,10 @@ def build_template(name: str, *, skip_cache: bool = False) -> str: .make_dir("/home/user/logs/verifier") .make_dir("/home/user/task") .make_dir("/home/user/workdir") - .make_dir("/home/user/proxy") - .copy(str(_PROXY_SOURCE), "/home/user/proxy/interception.py") .set_workdir("/home/user/workdir") ) if skip_cache: template = template.skip_cache() - info = Template.build( template, name, @@ -111,31 +60,15 @@ def build_template(name: str, *, skip_cache: bool = False) -> str: def main(argv: list[str] | None = None) -> int: p = argparse.ArgumentParser(prog="build_e2b_template") - p.add_argument( - "--name", - default="coding-agent-rl", - help="Template name (default: coding-agent-rl).", - ) - p.add_argument( - "--skip-cache", - action="store_true", - help="Force a clean rebuild, ignoring cache.", - ) + p.add_argument("--name", default="coding-agent-rl") + p.add_argument("--skip-cache", action="store_true") args = p.parse_args(argv) - _load_env(_REPO_ROOT / "envs" / "coding_agent_env" / "sandbox" / ".env") if not os.environ.get("E2B_API_KEY"): print("ERROR: E2B_API_KEY required.", file=sys.stderr) return 2 - - print(f"Building template '{args.name}' (proxy source: {_PROXY_SOURCE})") - print(f"Skip cache: {args.skip_cache}") - print() - template_id = build_template(args.name, skip_cache=args.skip_cache) - print() print(f"Built. Template id/name: {template_id}") - print(f"Use in code: Sandbox.create(template='{args.name}')") return 0 diff --git a/src/openenv/core/harness/agents/base.py b/src/openenv/core/harness/agents/base.py index 72cc9a6cf..ded9ba3b8 100644 --- a/src/openenv/core/harness/agents/base.py +++ b/src/openenv/core/harness/agents/base.py @@ -174,9 +174,6 @@ class CLIAgentSpec: mcp_config: MCPConfigSpec """How MCP tool configuration is injected.""" - supports_logprob_proxy: bool = True - """Whether this agent can be routed through the interception proxy.""" - default_timeout_s: float = 600.0 """Default per-rollout timeout in seconds.""" diff --git a/src/openenv/core/harness/agents/opencode.py b/src/openenv/core/harness/agents/opencode.py index d0146b008..13c17fa04 100644 --- a/src/openenv/core/harness/agents/opencode.py +++ b/src/openenv/core/harness/agents/opencode.py @@ -177,7 +177,6 @@ def _system_prompt_content(task: Any, config: Any) -> str | None: method="config_file", path_template="{home}/.config/opencode/opencode.json", ), - supports_logprob_proxy=True, default_timeout_s=900.0, setup=( "set -e && " diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py index 63e2eb0c3..d7b60569f 100644 --- a/src/openenv/core/harness/agents/pi.py +++ b/src/openenv/core/harness/agents/pi.py @@ -111,7 +111,6 @@ def _parse_events(line: str) -> AgentEvent | None: method="config_file", path_template="{workdir}/.mcp.json", ), - supports_logprob_proxy=True, default_timeout_s=600.0, setup=( "set -e && " diff --git a/src/openenv/core/harness/sandbox/interception.py b/src/openenv/core/harness/sandbox/interception.py deleted file mode 100644 index 4e7c857ac..000000000 --- a/src/openenv/core/harness/sandbox/interception.py +++ /dev/null @@ -1,660 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. - -"""Transparent OpenAI-compatible forwarding proxy with logprob capture. - -The proxy is a small FastAPI app that agent CLIs (OpenCode, Claude Code, -Codex, Pi, etc.) talk to instead of the upstream LLM endpoint. It: - -1. Forwards every ``POST /v1/chat/completions`` request to the real upstream - URL, injecting ``logprobs=true`` and ``top_logprobs=N`` so the upstream - returns per-token logprobs. -2. Captures each ``(request, response, logprobs)`` triple to a JSON-lines - trace file. -3. Returns the upstream response to the agent verbatim (minus the ``logprobs`` - field, which we strip so the CLI never sees anything unexpected). - -The proxy is stateless beyond the trace file. One proxy instance runs per -session, normally inside the sandbox on ``localhost:7000``. - -Run standalone:: - - UPSTREAM_API_KEY=... python -m openenv.core.harness.sandbox.interception \\ - --upstream-url https://vllm.example/v1 \\ - --trace /tmp/trace.jsonl \\ - --port 7000 -""" - -from __future__ import annotations - -import argparse -import asyncio -import copy -import json -import logging -import os -import socket -import threading -import time -from contextlib import asynccontextmanager, closing -from dataclasses import dataclass, field -from pathlib import Path -from typing import Any - -import httpx -import uvicorn -from fastapi import FastAPI, Request, Response -from fastapi.responses import JSONResponse, StreamingResponse - - -CHAT_COMPLETIONS_PATH = "/v1/chat/completions" -_LOG = logging.getLogger(__name__) - - -@dataclass -class ProxyConfig: - """Runtime configuration for one :class:`InterceptionProxy`.""" - - upstream_url: str - upstream_api_key: str = "intercepted" - trace_path: str = "/tmp/opencode-proxy-trace.jsonl" - host: str = "127.0.0.1" - port: int = 7000 - top_logprobs: int = 5 - request_timeout_s: float = 600.0 - # Cap ``max_tokens`` before forwarding. OpenCode historically asks for very - # large values (e.g. 32000) that exceed gpt-4o-mini's 16384 cap; capping - # here avoids spurious upstream 400s without requiring the caller to know - # per-model limits. - max_tokens_cap: int | None = 16384 - # Disable Qwen-style reasoning/thinking by injecting - # ``chat_template_kwargs.enable_thinking=false`` into forwarded requests. - disable_thinking: bool = False - # Override the ``model`` field on every forwarded request. Some opencode - # builds emit a stripped model id (e.g. ``Qwen3.5-4B`` instead of the - # ``Qwen/Qwen3.5-4B`` the upstream serves) for their internal - # title-generation call. Setting this to the exact upstream model id - # bypasses that mismatch. - model_override: str | None = None - - -@dataclass -class TurnRecord: - """One intercepted turn, written to the trace file as JSON-lines.""" - - turn: int - request: dict[str, Any] - response: dict[str, Any] - logprobs: list[dict[str, Any]] | None - completion_tokens: list[str] - completion_token_ids: list[int] - per_token_logps: list[float] - finish_reason: str | None - latency_s: float - timestamp: float = field(default_factory=time.time) - - def to_json(self) -> str: - return json.dumps(self.__dict__, default=str) - - -def _build_app(cfg: ProxyConfig) -> FastAPI: - """Construct the FastAPI app that serves one proxy session.""" - - state: dict[str, Any] = {"turn": 0, "lock": asyncio.Lock()} - - # HTTP client reused across requests. ``None`` auth header — we let each - # request carry its own ``Authorization`` populated from ``upstream_api_key``. - client = httpx.AsyncClient(timeout=cfg.request_timeout_s) - trace_file = open(cfg.trace_path, "a", buffering=1) - - @asynccontextmanager - async def lifespan(_: FastAPI) -> Any: - try: - yield - finally: - await client.aclose() - trace_file.close() - - app = FastAPI(title="opencode-interception-proxy", lifespan=lifespan) - - @app.get("/healthz") - def healthz() -> dict[str, str]: - return {"status": "ok"} - - @app.post(CHAT_COMPLETIONS_PATH) - async def chat_completions(request: Request) -> Response: - raw_body = await request.body() - try: - body = json.loads(raw_body) - except json.JSONDecodeError: - return JSONResponse(status_code=400, content={"error": "invalid json body"}) - - forwarded_body = _prepare_forwarded_body(body, cfg) - headers = { - "Content-Type": "application/json", - "Authorization": f"Bearer {cfg.upstream_api_key}", - } - upstream_url = _resolve_upstream_url(cfg.upstream_url) - - async with state["lock"]: - state["turn"] += 1 - turn_idx = state["turn"] - - if forwarded_body.get("stream"): - return await _proxy_streaming( - client=client, - upstream_url=upstream_url, - headers=headers, - forwarded_body=forwarded_body, - original_body=body, - trace_file=trace_file, - turn_idx=turn_idx, - ) - return await _proxy_unary( - client=client, - upstream_url=upstream_url, - headers=headers, - forwarded_body=forwarded_body, - original_body=body, - trace_file=trace_file, - turn_idx=turn_idx, - ) - - return app - - -def _prepare_forwarded_body(body: dict[str, Any], cfg: ProxyConfig) -> dict[str, Any]: - """Return the body we actually send upstream. - - - Injects ``logprobs=true`` + ``top_logprobs`` so the upstream emits - per-token logprobs. - - Caps ``max_tokens`` / ``max_completion_tokens`` to ``max_tokens_cap``. - - For models that reject ``max_tokens`` (e.g. gpt-5.x), translates to - ``max_completion_tokens``. - """ - forwarded = copy.deepcopy(body) - forwarded.setdefault("logprobs", True) - forwarded.setdefault("top_logprobs", cfg.top_logprobs) - - # GPT-5.x and newer: ``max_tokens`` is rejected; must use - # ``max_completion_tokens``. Detect via model string so we don't break - # gpt-4.x or vLLM-hosted models that accept ``max_tokens``. - model = str(forwarded.get("model", "")) - needs_translation = _model_uses_max_completion_tokens(model) - if needs_translation and "max_tokens" in forwarded: - value = forwarded.pop("max_tokens") - forwarded.setdefault("max_completion_tokens", value) - - if cfg.max_tokens_cap is not None: - for key in ("max_tokens", "max_completion_tokens"): - value = forwarded.get(key) - if isinstance(value, int) and value > cfg.max_tokens_cap: - forwarded[key] = cfg.max_tokens_cap - - if cfg.disable_thinking: - # vLLM applies chat_template_kwargs to the tokenizer's chat template - # for Qwen3/Qwen3.5 models, turning off ... generation. - extra = forwarded.setdefault("chat_template_kwargs", {}) - extra.setdefault("enable_thinking", False) - - if cfg.model_override: - forwarded["model"] = cfg.model_override - - return forwarded - - -def _model_uses_max_completion_tokens(model: str) -> bool: - """Heuristic: ``True`` for models that reject ``max_tokens``.""" - # Strip a provider prefix opencode may have prepended (e.g. "intercepted/"). - bare = model.split("/", 1)[-1].lower() - return bare.startswith(("gpt-5", "o1", "o3", "o4")) - - -def _resolve_upstream_url(upstream: str) -> str: - """Build the fully qualified chat-completions URL from a base URL.""" - base = upstream.rstrip("/") - if base.endswith("/v1"): - return f"{base}/chat/completions" - return f"{base}{CHAT_COMPLETIONS_PATH}" - - -async def _proxy_unary( - *, - client: httpx.AsyncClient, - upstream_url: str, - headers: dict[str, str], - forwarded_body: dict[str, Any], - original_body: dict[str, Any], - trace_file: Any, - turn_idx: int, -) -> Response: - start = time.time() - upstream_response = await client.post( - upstream_url, content=json.dumps(forwarded_body), headers=headers - ) - latency = time.time() - start - try: - response_json = upstream_response.json() - except Exception: - return Response( - content=upstream_response.content, - status_code=upstream_response.status_code, - media_type=upstream_response.headers.get( - "content-type", "application/json" - ), - ) - - record = _build_turn_record( - turn_idx=turn_idx, - request_body=forwarded_body, - response_json=response_json, - latency_s=latency, - ) - trace_file.write(record.to_json() + "\n") - sanitized = _strip_logprobs(response_json) - return JSONResponse(content=sanitized, status_code=upstream_response.status_code) - - -async def _proxy_streaming( - *, - client: httpx.AsyncClient, - upstream_url: str, - headers: dict[str, str], - forwarded_body: dict[str, Any], - original_body: dict[str, Any], - trace_file: Any, - turn_idx: int, -) -> Response: - """Forward an SSE stream while accumulating the full response. - - Opens the upstream stream and inspects the status. On non-2xx, reads the - full body (an error JSON, not SSE) and returns it to the caller as a - regular JSON response — previously we silently emitted an empty - ``text/event-stream`` which opencode interpreted as an empty assistant - turn. Both the error body and the latency are written to the trace file - so debugging a broken rollout doesn't require another round-trip. - """ - - start = time.time() - - # Open the stream outside the generator so we can branch on status before - # committing to a streaming response shape. - upstream_cm = client.stream( - "POST", - upstream_url, - content=json.dumps(forwarded_body), - headers=headers, - ) - upstream = await upstream_cm.__aenter__() - - if upstream.status_code >= 400: - # Upstream responded with an error body (not SSE). Read it fully and - # return as a non-streaming JSON payload. - error_bytes = await upstream.aread() - await upstream_cm.__aexit__(None, None, None) - latency = time.time() - start - try: - error_json = json.loads(error_bytes.decode() or "{}") - except Exception: - error_json = {"error": error_bytes.decode(errors="replace")[:4000]} - record = _build_turn_record( - turn_idx=turn_idx, - request_body=forwarded_body, - response_json={ - "choices": [], - "usage": None, - "upstream_status": upstream.status_code, - "upstream_error": error_json, - }, - latency_s=latency, - ) - trace_file.write(record.to_json() + "\n") - _LOG.warning( - "proxy turn %s: upstream %s: %s", - turn_idx, - upstream.status_code, - str(error_json)[:400], - ) - return JSONResponse(content=error_json, status_code=upstream.status_code) - - async def _stream() -> Any: - accumulated: dict[str, Any] = { - "content_by_idx": {}, - "tool_calls_by_idx": {}, - "finish_by_idx": {}, - "logprobs_by_idx": {}, - } - last_chunk: dict[str, Any] = {} - try: - async for line in upstream.aiter_lines(): - if not line: - yield "\n" - continue - yield line + "\n" - if not line.startswith("data:"): - continue - data = line[len("data:") :].strip() - if data == "[DONE]": - continue - try: - chunk = json.loads(data) - except json.JSONDecodeError: - continue - last_chunk = chunk - _accumulate_stream_chunk(chunk, accumulated) - finally: - await upstream_cm.__aexit__(None, None, None) - - latency = time.time() - start - response_json = _assemble_streamed_response(last_chunk, accumulated) - record = _build_turn_record( - turn_idx=turn_idx, - request_body=forwarded_body, - response_json=response_json, - latency_s=latency, - ) - trace_file.write(record.to_json() + "\n") - - return StreamingResponse(_stream(), media_type="text/event-stream") - - -def _accumulate_stream_chunk(chunk: dict[str, Any], acc: dict[str, Any]) -> None: - for choice in chunk.get("choices", []) or []: - idx = choice.get("index", 0) - delta = choice.get("delta") or {} - content = delta.get("content") - if content: - acc["content_by_idx"].setdefault(idx, []).append(content) - # HF-Router's Qwen thinking mode streams the chain-of-thought under a - # separate ``reasoning`` field (per Together/Scaleway). Accumulate it - # so the assembled response surfaces it — otherwise it's dropped and - # proxy_turn observability is lost for thinking-mode rollouts. - reasoning = delta.get("reasoning") - if reasoning: - acc.setdefault("reasoning_by_idx", {}).setdefault(idx, []).append(reasoning) - for tc in delta.get("tool_calls") or []: - tc_idx = tc.get("index", 0) - bucket = acc["tool_calls_by_idx"].setdefault( - (idx, tc_idx), - { - "id": None, - "type": "function", - "function": {"name": "", "arguments": ""}, - }, - ) - if tc.get("id"): - bucket["id"] = tc["id"] - fn = tc.get("function") or {} - if fn.get("name"): - bucket["function"]["name"] += fn["name"] - if fn.get("arguments"): - bucket["function"]["arguments"] += fn["arguments"] - if choice.get("finish_reason"): - acc["finish_by_idx"][idx] = choice["finish_reason"] - lp = choice.get("logprobs") or {} - content_lp = lp.get("content") - if content_lp: - acc["logprobs_by_idx"].setdefault(idx, []).extend(content_lp) - - -def _assemble_streamed_response( - last_chunk: dict[str, Any], acc: dict[str, Any] -) -> dict[str, Any]: - indices = sorted( - set(acc["content_by_idx"]) - | set(acc["finish_by_idx"]) - | {k[0] for k in acc["tool_calls_by_idx"]} - | set(acc["logprobs_by_idx"]) - | {0} - ) - choices: list[dict[str, Any]] = [] - for idx in indices: - tool_calls = [ - acc["tool_calls_by_idx"][k] - for k in sorted(acc["tool_calls_by_idx"]) - if k[0] == idx - ] - message: dict[str, Any] = {"role": "assistant"} - content = "".join(acc["content_by_idx"].get(idx, [])) - if content: - message["content"] = content - reasoning = "".join((acc.get("reasoning_by_idx") or {}).get(idx, [])) - if reasoning: - message["reasoning"] = reasoning - if tool_calls: - message["tool_calls"] = tool_calls - choice: dict[str, Any] = { - "index": idx, - "message": message, - "finish_reason": acc["finish_by_idx"].get(idx), - } - if acc["logprobs_by_idx"].get(idx): - choice["logprobs"] = {"content": acc["logprobs_by_idx"][idx]} - choices.append(choice) - return { - "id": last_chunk.get("id", ""), - "object": "chat.completion", - "model": last_chunk.get("model", ""), - "choices": choices, - "usage": last_chunk.get("usage"), - } - - -def _build_turn_record( - *, - turn_idx: int, - request_body: dict[str, Any], - response_json: dict[str, Any], - latency_s: float, -) -> TurnRecord: - """Extract per-token logprobs into a normalized :class:`TurnRecord`.""" - - choice = (response_json.get("choices") or [{}])[0] - logprobs_field = choice.get("logprobs") or {} - content_lp = logprobs_field.get("content") or [] - - tokens: list[str] = [] - token_ids: list[int] = [] - per_token_logps: list[float] = [] - for entry in content_lp: - tokens.append(entry.get("token", "")) - # OpenAI returns no raw token ids; vLLM returns them as ``token_id``. - token_id = entry.get("token_id") - if token_id is not None: - token_ids.append(int(token_id)) - lp = entry.get("logprob") - if lp is not None: - per_token_logps.append(float(lp)) - - return TurnRecord( - turn=turn_idx, - request=request_body, - response=response_json, - logprobs=content_lp, - completion_tokens=tokens, - completion_token_ids=token_ids, - per_token_logps=per_token_logps, - finish_reason=choice.get("finish_reason"), - latency_s=latency_s, - ) - - -def _strip_logprobs(response_json: dict[str, Any]) -> dict[str, Any]: - """Return a copy of the response with ``choices[*].logprobs`` removed.""" - - out = dict(response_json) - choices = out.get("choices") - if isinstance(choices, list): - out["choices"] = [ - {k: v for k, v in (ch or {}).items() if k != "logprobs"} for ch in choices - ] - return out - - -# --------------------------------------------------------------------------- -# Standalone runner (used inside the sandbox) -# --------------------------------------------------------------------------- - - -def serve(cfg: ProxyConfig) -> None: - """Start the proxy and block (for use as the sandbox-side entry point).""" - - app = _build_app(cfg) - uvicorn.run(app, host=cfg.host, port=cfg.port, log_level="warning") - - -class InterceptionProxy: - """Thread-backed controller for running the proxy locally. - - Used by unit tests and by any in-process driver that wants a short-lived - proxy on the local machine. Inside a sandbox we invoke :func:`serve` - directly via ``python -m openenv.core.harness.sandbox.interception``. - """ - - def __init__(self, cfg: ProxyConfig) -> None: - self._cfg = cfg - self._server: uvicorn.Server | None = None - self._thread: threading.Thread | None = None - self._ready = threading.Event() - - @property - def url(self) -> str: - return f"http://{self._cfg.host}:{self._cfg.port}/v1" - - @property - def config(self) -> ProxyConfig: - return self._cfg - - def start(self) -> None: - app = _build_app(self._cfg) - config = uvicorn.Config( - app, - host=self._cfg.host, - port=self._cfg.port, - log_level="warning", - lifespan="on", - ) - self._server = uvicorn.Server(config) - self._thread = threading.Thread(target=self._run_server, daemon=True) - self._thread.start() - # Wait for the server to accept connections. - deadline = time.time() + 10 - while time.time() < deadline: - if _port_open(self._cfg.host, self._cfg.port): - self._ready.set() - return - time.sleep(0.05) - raise RuntimeError("InterceptionProxy failed to start within 10s") - - def _run_server(self) -> None: - assert self._server is not None - self._server.run() - - def stop(self) -> None: - if self._server is None: - return - self._server.should_exit = True - if self._thread is not None: - self._thread.join(timeout=5) - self._server = None - self._thread = None - - def __enter__(self) -> "InterceptionProxy": - self.start() - return self - - def __exit__(self, *exc) -> None: - self.stop() - - -def _port_open(host: str, port: int) -> bool: - with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: - s.settimeout(0.2) - return s.connect_ex((host, port)) == 0 - - -# --------------------------------------------------------------------------- -# Trace reader (used by the session to pull captured turns back) -# --------------------------------------------------------------------------- - - -def read_trace(path: str | os.PathLike) -> list[dict[str, Any]]: - """Read a proxy trace file into a list of dicts.""" - - trace: list[dict[str, Any]] = [] - p = Path(path) - if not p.exists(): - return trace - for line in p.read_text().splitlines(): - line = line.strip() - if not line: - continue - trace.append(json.loads(line)) - return trace - - -# --------------------------------------------------------------------------- -# CLI entry point -# --------------------------------------------------------------------------- - - -def main() -> None: - parser = argparse.ArgumentParser(prog="openenv.core.harness.sandbox.interception") - parser.add_argument("--upstream-url", required=True) - parser.add_argument( - "--upstream-api-key", - default=None, - help=( - "Upstream API key. Prefer OPENCODE_UPSTREAM_API_KEY so the key " - "does not appear in process argv." - ), - ) - parser.add_argument("--trace", default="/tmp/opencode-proxy-trace.jsonl") - parser.add_argument("--host", default="127.0.0.1") - parser.add_argument("--port", type=int, default=7000) - parser.add_argument("--top-logprobs", type=int, default=5) - parser.add_argument("--request-timeout", type=float, default=600.0) - parser.add_argument( - "--max-tokens-cap", - type=int, - default=None, - help="Clamp max_tokens/max_completion_tokens on forwarded requests.", - ) - parser.add_argument( - "--disable-thinking", - action="store_true", - help="Inject chat_template_kwargs.enable_thinking=false (Qwen3/Qwen3.5).", - ) - parser.add_argument( - "--model-override", - default=None, - help="Rewrite the `model` field on every forwarded request.", - ) - args = parser.parse_args() - upstream_api_key = ( - args.upstream_api_key - or os.environ.get("OPENCODE_UPSTREAM_API_KEY") - or os.environ.get("UPSTREAM_API_KEY") - or "intercepted" - ) - - cfg = ProxyConfig( - upstream_url=args.upstream_url, - upstream_api_key=upstream_api_key, - trace_path=args.trace, - host=args.host, - port=args.port, - top_logprobs=args.top_logprobs, - request_timeout_s=args.request_timeout, - max_tokens_cap=args.max_tokens_cap, - disable_thinking=args.disable_thinking, - model_override=args.model_override, - ) - serve(cfg) - - -if __name__ == "__main__": - main() From 71bd9e96326d2618ba3c28646d803faa4ec4f9b3 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 15 May 2026 23:35:44 +0530 Subject: [PATCH 12/79] feat: InterceptionServer + interception_gate mode for trainer-owned generation InterceptionServer (aiohttp) runs on the trainer host. Each rollout registers a queue. The agent's OPENAI_BASE_URL points at `{base_url}/rollout/{id}/v1`. When the agent makes an LLM call it blocks at the server. The training loop dequeues the request, calls vLLM with logprobs=True and return_token_ids=True, and delivers the response back via deliver_response(). --- pyproject.toml | 1 + src/openenv/core/harness/agents/__init__.py | 4 + src/openenv/core/harness/agents/cli_driver.py | 444 +++++------------- .../harness/agents/interception_server.py | 324 +++++++++++++ 4 files changed, 445 insertions(+), 328 deletions(-) create mode 100644 src/openenv/core/harness/agents/interception_server.py diff --git a/pyproject.toml b/pyproject.toml index 08f1bb6d3..e40b79c9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dependencies = [ # Web UI dependencies "gradio>=4.0.0", "httpx>=0.28.1", + "aiohttp>=3.13.5", ] [project.optional-dependencies] diff --git a/src/openenv/core/harness/agents/__init__.py b/src/openenv/core/harness/agents/__init__.py index 8ef31976b..b715582a4 100644 --- a/src/openenv/core/harness/agents/__init__.py +++ b/src/openenv/core/harness/agents/__init__.py @@ -28,6 +28,7 @@ CLIAgentSpec, MCPConfigSpec, ) +from .interception_server import deliver_response, InterceptionServer # Registry @@ -104,4 +105,7 @@ def _auto_import(name: str) -> None: "ArtifactSpec", "CLIAgentSpec", "MCPConfigSpec", + # Interception gate + "InterceptionServer", + "deliver_response", ] diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 42ac460f1..0e58af9e0 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -6,26 +6,24 @@ """Shared CLI agent driver, session, and session factory. -The :class:`CLIAgentDriver` factors out the common 70% of CLI harness -lifecycle — sandbox creation, MCP config injection, interception proxy -setup, subprocess management, and result collection. +Two modes are supported: -It is **fully generic**: it reads the :class:`CLIAgentSpec`'s declarative -data fields and executes them mechanically. No per-agent code lives here. - -The :class:`CLIAgentSession` implements :class:`ResourceSession` and -the :class:`CLIAgentSessionFactory` implements :class:`ResourceSessionFactory`, -so the CLI agent driver integrates seamlessly with the existing harness -runtime from PR #603. +- ``black_box`` — the agent talks directly to the upstream LLM. No logprob + capture. For eval and demos. +- ``interception_gate`` — the agent's LLM calls are routed to an + :class:`InterceptionServer` running on the trainer host. The training + loop owns the forward pass and delivers responses back. For RL training. """ from __future__ import annotations +import asyncio import json import logging import shlex +import threading import time -from pathlib import Path +import uuid from typing import Any, Callable, Literal from openenv.core.env_server.mcp_types import Tool @@ -39,37 +37,16 @@ from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle from .base import CLIAgentSpec +from .interception_server import deliver_response, InterceptionServer _log = logging.getLogger(__name__) -# Interception proxy defaults -_PROXY_PORT = 7000 -_PROXY_TRACE_PATH = "/home/user/logs/agent/proxy_trace.jsonl" -_PROXY_LOG_PATH = "/home/user/logs/agent/proxy.log" - -# Where the proxy source lives on disk. Uploaded into sandboxes that don't -# already have it baked in. -_PROXY_SOURCE_PATH = Path(__file__).resolve().parents[1] / "sandbox" / "interception.py" - -# Verifier type — callable that checks the agent's work and returns a result Verifier = Callable[..., VerifyResult] -# CLIAgentSession - - class CLIAgentSession(ResourceSession): - """Per-rollout session wrapping one sandbox with one running agent CLI. - - The session is created already-running: :meth:`CLIAgentSessionFactory.create` - launches the agent before returning. Typical usage:: - - session = factory.create(task) - session.wait_for_completion() - result = session.verify([]) - session.close() - """ + """Per-rollout session wrapping one sandbox with one running agent CLI.""" def __init__( self, @@ -80,9 +57,10 @@ def __init__( config: Any, verifier: Verifier | None = None, base_url_override: str | None = None, - proxy_trace_path: str | None = None, - proxy_bg_job: BgJob | None = None, agent_bg_job: BgJob | None = None, + interception_server: InterceptionServer | None = None, + interception_rollout_id: str | None = None, + interception_queue: asyncio.Queue | None = None, ) -> None: self.spec = spec self.sandbox = sandbox @@ -90,11 +68,10 @@ def __init__( self.config = config self._verifier = verifier self._base_url_override = base_url_override - self._proxy_trace_path = proxy_trace_path - self._proxy_bg_job = proxy_bg_job self._agent_bg_job = agent_bg_job - - # ResourceSession contract + self._interception_server = interception_server + self._interception_rollout_id = interception_rollout_id + self._interception_queue = interception_queue def initial_messages(self) -> list[Message]: instruction = ( @@ -105,7 +82,6 @@ def initial_messages(self) -> list[Message]: return [{"role": "user", "content": instruction}] def list_tools(self) -> list[Tool]: - # CLI agents own their own tool loop — none are exposed to the harness. return [] def call_tool(self, name: str, arguments: dict[str, Any]) -> ToolResult: @@ -132,16 +108,13 @@ def close(self) -> None: except Exception: pass self._agent_bg_job = None - if self._proxy_bg_job is not None: - try: - self._proxy_bg_job.kill() - except Exception: - pass - self._proxy_bg_job = None + if ( + self._interception_server is not None + and self._interception_rollout_id is not None + ): + self._interception_server.unregister_rollout(self._interception_rollout_id) self.sandbox.kill() - # CLI-agent-specific API - def wait_for_completion(self, timeout_s: float | None = None) -> int: """Block until the agent exits, returning its exit code.""" budget = timeout_s if timeout_s is not None else self.spec.default_timeout_s @@ -152,11 +125,7 @@ def wait_for_completion(self, timeout_s: float | None = None) -> int: return self._agent_bg_job.wait(timeout=budget) def collect_artifacts(self) -> dict[str, Any]: - """Collect all artifacts declared in ``spec.artifacts`` from the sandbox. - - Returns a dict keyed by artifact name. Missing optional artifacts are - silently skipped. - """ + """Collect all artifacts declared in ``spec.artifacts`` from the sandbox.""" result: dict[str, Any] = {} if not self.spec.artifacts: return result @@ -166,9 +135,6 @@ def collect_artifacts(self) -> dict[str, Any]: if artifact_spec.format == "json": result[name] = json.loads(content) elif artifact_spec.format == "jsonl": - # Parse valid JSON lines, skip non-JSON preamble - # (e.g. opencode emits database migration messages - # before the first JSON event). records = [] for line in content.splitlines(): line = line.strip() @@ -195,72 +161,85 @@ def collect_artifacts(self) -> dict[str, Any]: ) return result - def fetch_proxy_trace(self) -> list[dict[str, Any]]: - """Return per-turn proxy-captured records (transparent_proxy mode only). + # interception_gate API - Each entry has ``request``, ``response``, ``completion_tokens``, - ``completion_token_ids``, ``per_token_logps``, ``finish_reason``, - and ``latency_s``. Returns ``[]`` in black_box mode. + async def next_request( + self, timeout_s: float | None = None + ) -> dict[str, Any] | None: + """Await the next LLM request from the agent (interception_gate only). + + Returns the intercept dict, or ``None`` when the agent has exited. """ - if self._proxy_trace_path is None: - return [] - try: - content = self.sandbox.read_text(self._proxy_trace_path) - except Exception: - return [] - records: list[dict[str, Any]] = [] - for line in content.splitlines(): - line = line.strip() - if not line: + if self._interception_queue is None: + raise RuntimeError( + "next_request() is only available in interception_gate mode." + ) + server = self._interception_server + assert server is not None + + deadline = time.time() + (timeout_s or self.spec.default_timeout_s) + while True: + remaining = deadline - time.time() + if remaining <= 0: + raise TimeoutError( + f"{self.spec.name} interception_gate: no request within timeout" + ) + try: + request_id = await asyncio.wait_for( + self._interception_queue.get(), + timeout=min(remaining, 1.0), + ) + return server.intercepts[request_id] + except asyncio.TimeoutError: + if self._agent_bg_job is not None: + done_event = getattr(self._agent_bg_job, "_done", None) + if ( + done_event is not None + and isinstance(done_event, threading.Event) + and done_event.is_set() + ): + return None continue - records.append(json.loads(line)) - return records - -# CLIAgentDriver — shared lifecycle + async def deliver( + self, intercept: dict[str, Any], response_dict: dict[str, Any] + ) -> None: + """Return a trainer-generated response to the waiting agent.""" + await deliver_response(intercept, response_dict) class CLIAgentDriver: - """Shared driver for all CLI-based agentic harnesses. - - Implements the common lifecycle: - - 1. Create sandbox (via :class:`SandboxBackend`) - 2. Wait for sandbox ready (``echo ok`` probe) - 3. Install agent CLI — run ``spec.setup`` commands (skipped if - ``spec.install_check_cmd`` succeeds, i.e. pre-baked template) - 4. Upload ``spec.files`` into the sandbox - 5. Write MCP config (via ``spec.build_mcp_config``) - 6. Set environment variables from ``spec.env`` (with placeholder - resolution) - 7. Optionally start interception proxy (transparent_proxy mode) - 8. Build CLI command (via ``spec.build_command``) - 9. Launch agent as bg process - 10. Return a :class:`CLIAgentSession` - """ + """Shared driver for all CLI-based agentic harnesses.""" def __init__( self, spec: CLIAgentSpec, sandbox_backend: SandboxBackend, - mode: Literal["black_box", "transparent_proxy"] = "black_box", + mode: Literal["black_box", "interception_gate"] = "black_box", *, install_timeout_s: int = 240, setup_timeout_s: int = 300, - proxy_top_logprobs: int = 5, - proxy_max_tokens_cap: int | None = 16384, - proxy_disable_thinking: bool = False, + interception_server: InterceptionServer | None = None, + interception_base_url: str | None = None, ) -> None: - if mode not in {"black_box", "transparent_proxy"}: + if mode not in {"black_box", "interception_gate"}: raise ValueError(f"Unknown mode: {mode!r}") + if mode == "interception_gate": + if interception_server is None: + raise ValueError( + "interception_gate mode requires an InterceptionServer instance." + ) + if interception_base_url is None: + raise ValueError( + "interception_gate mode requires interception_base_url." + ) self.spec = spec self.sandbox_backend = sandbox_backend self.mode = mode self._install_timeout_s = install_timeout_s self._setup_timeout_s = setup_timeout_s - self._proxy_top_logprobs = proxy_top_logprobs - self._proxy_max_tokens_cap = proxy_max_tokens_cap - self._proxy_disable_thinking = proxy_disable_thinking + self._interception_server = interception_server + self._interception_base_url = interception_base_url def create_session( self, @@ -271,35 +250,16 @@ def create_session( seed: int | None = None, episode_id: str | None = None, ) -> CLIAgentSession: - """Create a fully bootstrapped session with a running agent. - - This is the main entry point. It: - 1. Creates a sandbox - 2. Bootstraps it (install agent, upload files, write MCP config) - 3. Optionally starts the interception proxy - 4. Launches the agent subprocess - 5. Returns a ready-to-use :class:`CLIAgentSession` - """ timeout_s = ( config.agent_timeout_s if hasattr(config, "agent_timeout_s") else self.spec.default_timeout_s ) sandbox_timeout = int(timeout_s) + 300 - - _log.info( - "%s driver: creating sandbox timeout=%ds mode=%s", - self.spec.name, - sandbox_timeout, - self.mode, - ) sandbox = self.sandbox_backend.create( timeout_s=sandbox_timeout, metadata={"episode_id": episode_id} if episode_id else None, ) - sid = getattr(sandbox, "sandbox_id", "?") - _log.info("%s driver: sandbox=%s — bootstrapping…", self.spec.name, sid) - try: self._bootstrap_sandbox(sandbox, task, config) except Exception as exc: @@ -308,33 +268,21 @@ def create_session( raise base_url_override: str | None = None - proxy_trace_path: str | None = None - proxy_bg_job: BgJob | None = None - - if self.mode == "transparent_proxy": - base_url = config.base_url if hasattr(config, "base_url") else "" - api_key = config.api_key if hasattr(config, "api_key") else "intercepted" - model = config.model if hasattr(config, "model") else "" - - _log.info( - "%s driver: starting interception proxy on :%d → %s", - self.spec.name, - _PROXY_PORT, - base_url, - ) - proxy_bg_job, base_url_override, proxy_trace_path = self._start_proxy( - sandbox, - base_url=base_url, - api_key=api_key, - model=model, + interception_rollout_id: str | None = None + interception_queue: asyncio.Queue | None = None + + if self.mode == "interception_gate": + assert self._interception_server is not None + assert self._interception_base_url is not None + rollout_id = episode_id or f"rollout_{uuid.uuid4().hex[:8]}" + interception_rollout_id = rollout_id + interception_queue = self._interception_server.register_rollout(rollout_id) + base_url_override = ( + f"{self._interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1" ) - _log.info("%s driver: proxy up at %s", self.spec.name, base_url_override) agent_bg_job = self._start_agent( - sandbox, - task, - config, - base_url_override=base_url_override, + sandbox, task, config, base_url_override=base_url_override ) return CLIAgentSession( @@ -344,35 +292,20 @@ def create_session( config=config, verifier=verifier, base_url_override=base_url_override, - proxy_trace_path=proxy_trace_path, - proxy_bg_job=proxy_bg_job, agent_bg_job=agent_bg_job, + interception_server=self._interception_server, + interception_rollout_id=interception_rollout_id, + interception_queue=interception_queue, ) - # Bootstrap stages - def _bootstrap_sandbox( - self, - sandbox: SandboxHandle, - task: Any, - config: Any, + self, sandbox: SandboxHandle, task: Any, config: Any ) -> None: - """Install agent, upload files, write MCP config.""" - - # Stage 1: wait for sandbox readiness self._wait_for_sandbox_ready(sandbox) - - # Stage 2: install agent CLI (skip if pre-baked) if not self._agent_already_installed(sandbox): self._install_agent(sandbox) - - # Stage 3: upload spec.files self._upload_files(sandbox, task, config) - - # Stage 4: write MCP config (if the spec provides a builder) self._write_mcp_config(sandbox, config) - - # Stage 5: run task.setup_shell if present setup_shell = task.setup_shell if hasattr(task, "setup_shell") else None if setup_shell: r = sandbox.exec(setup_shell, timeout=self._setup_timeout_s) @@ -382,13 +315,8 @@ def _bootstrap_sandbox( ) def _wait_for_sandbox_ready( - self, - sandbox: SandboxHandle, - *, - attempts: int = 15, - delay_s: float = 1.0, + self, sandbox: SandboxHandle, *, attempts: int = 15, delay_s: float = 1.0 ) -> None: - """Probe sandbox until ``echo ok`` succeeds.""" last_err = "" for _ in range(attempts): try: @@ -405,7 +333,6 @@ def _wait_for_sandbox_ready( ) def _agent_already_installed(self, sandbox: SandboxHandle) -> bool: - """Check if the agent CLI is already available in the sandbox.""" cmd = " ".join(shlex.quote(c) for c in self.spec.install_check_cmd) try: r = sandbox.exec(cmd, timeout=10) @@ -414,11 +341,9 @@ def _agent_already_installed(self, sandbox: SandboxHandle) -> bool: return False def _install_agent(self, sandbox: SandboxHandle) -> None: - """Run ``spec.setup`` commands to install the agent CLI.""" if self.spec.setup is None: raise RuntimeError( - f"Agent {self.spec.name!r} is not installed in the sandbox " - "and no setup commands are provided in the spec." + f"Agent {self.spec.name!r} is not installed and no setup commands provided." ) commands = ( [self.spec.setup] if isinstance(self.spec.setup, str) else self.spec.setup @@ -433,34 +358,22 @@ def _install_agent(self, sandbox: SandboxHandle) -> None: label=f"{self.spec.name} install", ) - def _upload_files( - self, - sandbox: SandboxHandle, - task: Any, - config: Any, - ) -> None: - """Upload ``spec.files`` into the sandbox, resolving callables.""" + def _upload_files(self, sandbox: SandboxHandle, task: Any, config: Any) -> None: if not self.spec.files: return for path, content_or_fn in self.spec.files.items(): - if callable(content_or_fn): - content = content_or_fn(task, config) - else: - content = content_or_fn + content = ( + content_or_fn(task, config) + if callable(content_or_fn) + else content_or_fn + ) if content is not None: sandbox.write_text(path, content) - - # Also upload task.upload_files if the task has them. upload_files = task.upload_files if hasattr(task, "upload_files") else {} for path, content in upload_files.items(): sandbox.write_text(path, content) - def _write_mcp_config( - self, - sandbox: SandboxHandle, - config: Any, - ) -> None: - """Write MCP configuration using the spec's builder.""" + def _write_mcp_config(self, sandbox: SandboxHandle, config: Any) -> None: if self.spec.build_mcp_config is None: return if ( @@ -476,15 +389,12 @@ def _write_mcp_config( config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" ) mcp_path = self.spec.mcp_config.path_template.format( - workdir=workdir, - home=home, + workdir=workdir, home=home ) mcp_content = self.spec.build_mcp_config(self.spec, [], workdir) if mcp_content: sandbox.write_text(mcp_path, mcp_content) - # Agent launch - def _start_agent( self, sandbox: SandboxHandle, @@ -493,17 +403,14 @@ def _start_agent( *, base_url_override: str | None = None, ) -> BgJob: - """Build CLI command, resolve env vars, and launch as bg process.""" - # Build command via spec hook if self.spec.build_command is not None: cmd = self.spec.build_command(self.spec, config, task, None) else: cmd = " ".join(shlex.quote(c) for c in self.spec.base_command) - - # Resolve environment variables envs = self._resolve_env_vars(config, base_url_override=base_url_override) - - _log.info("%s driver: launching agent", self.spec.name) + if self.mode == "interception_gate" and self._interception_server is not None: + envs["OPENAI_API_KEY"] = self._interception_server.secret + envs["ANTHROPIC_API_KEY"] = self._interception_server.secret return sandbox.start_bg(cmd, envs=envs) def _resolve_env_vars( @@ -512,124 +419,24 @@ def _resolve_env_vars( *, base_url_override: str | None = None, ) -> dict[str, str]: - """Build the env var dict for the agent process. - - If ``spec.build_env_vars`` is provided, delegate to it. - Otherwise resolve ``{placeholder}`` substitutions in ``spec.env``. - """ if self.spec.build_env_vars is not None: return self.spec.build_env_vars(self.spec, config) - if not self.spec.env: return {} - base_url = base_url_override or ( config.base_url if hasattr(config, "base_url") else "" ) api_key = config.api_key if hasattr(config, "api_key") else "intercepted" model = config.model if hasattr(config, "model") else "" - - substitutions = { - "base_url": base_url, - "api_key": api_key, - "model": model, - } - + substitutions = {"base_url": base_url, "api_key": api_key, "model": model} resolved: dict[str, str] = {} for key, value in self.spec.env.items(): try: resolved[key] = value.format(**substitutions) except KeyError: - # If a placeholder isn't in our substitutions, keep it as-is. resolved[key] = value return resolved - # Interception proxy - - def _start_proxy( - self, - sandbox: SandboxHandle, - *, - base_url: str, - api_key: str, - model: str, - ) -> tuple[BgJob, str, str]: - """Install deps, start proxy as bg job, wait for healthz. - - Returns ``(proxy_bg_job, base_url_override, proxy_trace_path)``. - """ - proxy_already_present = sandbox.exists("/home/user/proxy/interception.py") - - if not proxy_already_present: - self._exec_with_retry( - sandbox, - "pip install --quiet 'fastapi>=0.104' 'uvicorn[standard]>=0.24' " - "'httpx>=0.27' 2>&1 | tail -20", - timeout=180, - attempts=3, - backoff_s=2.0, - label="proxy deps install", - ) - sandbox.write_text( - "/home/user/proxy/interception.py", - _PROXY_SOURCE_PATH.read_text(), - ) - sandbox.write_text("/home/user/proxy/__init__.py", "") - - proxy_args = [ - "python", - "interception.py", - "--upstream-url", - base_url, - "--trace", - _PROXY_TRACE_PATH, - "--port", - str(_PROXY_PORT), - "--top-logprobs", - str(self._proxy_top_logprobs), - ] - if self._proxy_max_tokens_cap is not None: - proxy_args.extend(["--max-tokens-cap", str(self._proxy_max_tokens_cap)]) - if self._proxy_disable_thinking: - proxy_args.append("--disable-thinking") - if model: - proxy_args.extend(["--model-override", model]) - - quoted = " ".join(shlex.quote(a) for a in proxy_args) - proxy_cmd = ( - f"cd /home/user/proxy && {quoted} > {shlex.quote(_PROXY_LOG_PATH)} 2>&1" - ) - proxy_env = {"OPENCODE_UPSTREAM_API_KEY": api_key} - proxy_job = sandbox.start_bg(proxy_cmd, envs=proxy_env) - - # Wait for proxy healthz - attempts = 120 - interval_s = 0.5 - for _ in range(attempts): - r = sandbox.exec( - f"curl -sf http://127.0.0.1:{_PROXY_PORT}/healthz", - timeout=5, - ) - if r.exit_code == 0: - break - time.sleep(interval_s) - else: - log_content = "" - try: - log_content = sandbox.read_text(_PROXY_LOG_PATH) - except Exception: - pass - proxy_job.kill() - raise RuntimeError( - f"proxy did not start within {attempts * interval_s:.0f}s. " - f"log:\n{log_content[-2000:]}" - ) - - override_url = f"http://127.0.0.1:{_PROXY_PORT}/v1" - return proxy_job, override_url, _PROXY_TRACE_PATH - - # Utilities - def _exec_with_retry( self, sandbox: SandboxHandle, @@ -640,7 +447,6 @@ def _exec_with_retry( backoff_s: float = 3.0, label: str = "cmd", ) -> Any: - """Run ``sandbox.exec`` with exponential backoff on transient failure.""" last_stdout = "" last_stderr = "" last_exit = 0 @@ -666,30 +472,19 @@ def _exec_with_retry( ) -# CLIAgentSessionFactory - - class CLIAgentSessionFactory(ResourceSessionFactory): - """Factory that produces :class:`CLIAgentSession` instances for any - registered agent. - - Wraps :class:`CLIAgentDriver` to satisfy the - :class:`ResourceSessionFactory` contract from PR #603. - """ - def __init__( self, *, spec: CLIAgentSpec, config: Any, sandbox_backend: SandboxBackend, - mode: Literal["black_box", "transparent_proxy"] = "black_box", + mode: Literal["black_box", "interception_gate"] = "black_box", verifier: Verifier | None = None, install_timeout_s: int = 240, setup_timeout_s: int = 300, - proxy_top_logprobs: int = 5, - proxy_max_tokens_cap: int | None = 16384, - proxy_disable_thinking: bool = False, + interception_server: InterceptionServer | None = None, + interception_base_url: str | None = None, ) -> None: self._spec = spec self._config = config @@ -700,9 +495,8 @@ def __init__( mode=mode, install_timeout_s=install_timeout_s, setup_timeout_s=setup_timeout_s, - proxy_top_logprobs=proxy_top_logprobs, - proxy_max_tokens_cap=proxy_max_tokens_cap, - proxy_disable_thinking=proxy_disable_thinking, + interception_server=interception_server, + interception_base_url=interception_base_url, ) def create( @@ -711,7 +505,6 @@ def create( seed: int | None = None, episode_id: str | None = None, ) -> CLIAgentSession: - """Create one isolated session for a rollout.""" return self._driver.create_session( task=task, config=self._config, @@ -721,9 +514,4 @@ def create( ) -__all__ = [ - "CLIAgentDriver", - "CLIAgentSession", - "CLIAgentSessionFactory", - "Verifier", -] +__all__ = ["CLIAgentDriver", "CLIAgentSession", "CLIAgentSessionFactory", "Verifier"] diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py new file mode 100644 index 000000000..a075ec8b4 --- /dev/null +++ b/src/openenv/core/harness/agents/interception_server.py @@ -0,0 +1,324 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Host-side interception server for trainer-owned generation. + +The :class:`InterceptionServer` runs on the trainer node, outside any +sandbox. Each sandbox's agent is pointed at:: + + http://:/rollout//v1 + +When the agent makes an LLM call it blocks at this server. The training +loop calls :meth:`~InterceptionServer.register_rollout` to get a queue, +``await queue.get()`` to dequeue the pending request, runs its own vLLM +forward pass, then calls :func:`deliver_response` to unblock the agent. + +The caller is responsible for making the server reachable from the sandbox. +For Docker sandboxes on the same machine, ``host.docker.internal:`` +works. For remote sandboxes (E2B, HF Sandbox), set up your own tunnel +(ngrok, frp, public IP, VPN) and pass the URL as +``interception_base_url``. + +Usage — training loop:: + + server = InterceptionServer(port=8765) + await server.start() + + # Make the server reachable — your responsibility. + # Docker: base_url = f"http://host.docker.internal:{server.port}" + # Remote: base_url = your_tunnel_or_public_url + + queue = server.register_rollout(rollout_id) + # Agent runs with OPENAI_BASE_URL = f"{base_url}/rollout/{rollout_id}/v1" + + while True: + request_id = await asyncio.wait_for(queue.get(), timeout=...) + intercept = server.intercepts[request_id] + response = await vllm.generate(intercept["messages"], ...) + await deliver_response(intercept, response) + + server.unregister_rollout(rollout_id) + await server.stop() +""" + +from __future__ import annotations + +import asyncio +import hmac +import json +import logging +import secrets +import time +import uuid +from typing import Any + +from aiohttp import web + + +_log = logging.getLogger(__name__) + +_KEEPALIVE_INTERVAL_S = 3.0 +_MAX_REQUEST_BODY = 16 * 1024 * 1024 + + +class InterceptionServer: + """Async HTTP server that gates every LLM call from sandboxed agents. + + One shared instance handles all concurrent rollouts. Each rollout is + identified by a ``rollout_id`` in the URL path. + """ + + def __init__(self, port: int = 0, secret: str | None = None) -> None: + self.port = port + self.secret = secret or secrets.token_urlsafe(32) + self._app: web.Application | None = None + self._runner: web.AppRunner | None = None + self._site: web.TCPSite | None = None + self._lock = asyncio.Lock() + self.active_rollouts: dict[str, dict[str, Any]] = {} + self.intercepts: dict[str, dict[str, Any]] = {} + + async def start(self) -> None: + async with self._lock: + if self._app is not None: + return + app = web.Application(client_max_size=_MAX_REQUEST_BODY) + app.router.add_post( + "/rollout/{rollout_id}/v1/chat/completions", + self._handle_chat_completions, + ) + app.router.add_get("/health", self._handle_health) + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, "0.0.0.0", self.port) + await site.start() + if self.port == 0: + server = getattr(site, "_server", None) + sockets = getattr(server, "sockets", None) if server else None + if sockets: + self.port = sockets[0].getsockname()[1] + if self.port == 0: + raise RuntimeError("Failed to resolve OS-assigned port") + self._app = app + self._runner = runner + self._site = site + _log.info("InterceptionServer listening on :%d", self.port) + + async def stop(self) -> None: + async with self._lock: + if self._runner is None: + return + for intercept in list(self.intercepts.values()): + fut: asyncio.Future | None = intercept.get("response_future") + if fut and not fut.done(): + fut.cancel() + cq: asyncio.Queue | None = intercept.get("chunk_queue") + if cq is not None: + try: + cq.put_nowait(None) + except asyncio.QueueFull: + pass + self.intercepts.clear() + self.active_rollouts.clear() + try: + await self._runner.cleanup() + except RuntimeError: + pass + self._runner = None + self._site = None + self._app = None + + def register_rollout( + self, + rollout_id: str, + state: dict[str, Any] | None = None, + ) -> asyncio.Queue: + queue: asyncio.Queue = asyncio.Queue() + self.active_rollouts[rollout_id] = { + "request_id_queue": queue, + "state": state, + } + return queue + + def unregister_rollout(self, rollout_id: str) -> None: + for request_id in list(self.intercepts): + intercept = self.intercepts.get(request_id) + if intercept and intercept.get("rollout_id") == rollout_id: + fut: asyncio.Future | None = intercept.get("response_future") + if fut and not fut.done(): + fut.cancel() + cq: asyncio.Queue | None = intercept.get("chunk_queue") + if cq is not None: + try: + cq.put_nowait(None) + except asyncio.QueueFull: + pass + del self.intercepts[request_id] + self.active_rollouts.pop(rollout_id, None) + + def _authorized(self, request: web.Request) -> bool: + auth = request.headers.get("Authorization", "") + api_key = request.headers.get("x-api-key", "") + return hmac.compare_digest( + auth, f"Bearer {self.secret}" + ) or hmac.compare_digest(api_key, self.secret) + + async def _handle_health(self, request: web.Request) -> web.Response: + return web.json_response({"status": "ok"}) + + async def _handle_chat_completions( + self, request: web.Request + ) -> web.StreamResponse | web.Response: + if not self._authorized(request): + return web.json_response({"error": "Unauthorized"}, status=401) + + rollout_id = request.match_info["rollout_id"] + context = self.active_rollouts.get(rollout_id) + if not context: + return web.json_response({"error": "rollout not found"}, status=404) + + try: + body = await request.json() + except Exception as exc: + return web.json_response({"error": f"invalid JSON: {exc}"}, status=400) + + is_streaming = bool(body.get("stream")) + request_id = f"req_{uuid.uuid4().hex[:8]}" + chunk_queue: asyncio.Queue | None = asyncio.Queue() if is_streaming else None + + intercept: dict[str, Any] = { + "request_id": request_id, + "rollout_id": rollout_id, + "messages": body.get("messages"), + "model": body.get("model"), + "tools": body.get("tools"), + "stream": is_streaming, + "chunk_queue": chunk_queue, + "response_future": asyncio.get_event_loop().create_future(), + "body": body, + } + self.intercepts[request_id] = intercept + await context["request_id_queue"].put(request_id) + + if is_streaming: + return await self._stream_response(request, intercept) + + try: + response_dict = await intercept["response_future"] + except asyncio.CancelledError: + return web.json_response({"error": "rollout cancelled"}, status=499) + except Exception as exc: + return web.json_response({"error": str(exc)}, status=500) + + return web.json_response(response_dict) + + async def _stream_response( + self, request: web.Request, intercept: dict[str, Any] + ) -> web.StreamResponse: + chunk_queue: asyncio.Queue = intercept["chunk_queue"] + resp = web.StreamResponse( + status=200, + headers={ + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + "Connection": "keep-alive", + }, + ) + await resp.prepare(request) + get_task: asyncio.Task | None = None + try: + while True: + if get_task is None: + get_task = asyncio.create_task(chunk_queue.get()) + done, _ = await asyncio.wait({get_task}, timeout=_KEEPALIVE_INTERVAL_S) + if get_task not in done: + await resp.write(b": keepalive\n\n") + continue + chunk = get_task.result() + get_task = None + if chunk is None: + await resp.write(b"data: [DONE]\n\n") + break + await resp.write(f"data: {json.dumps(chunk)}\n\n".encode()) + await asyncio.sleep(0) + except (asyncio.CancelledError, ConnectionResetError): + pass + finally: + if get_task and not get_task.done(): + get_task.cancel() + try: + await resp.write_eof() + except Exception: + pass + return resp + + +async def deliver_response( + intercept: dict[str, Any], response_dict: dict[str, Any] +) -> None: + """Unblock the agent's HTTP handler with ``response_dict``. + + For non-streaming requests, resolves the future directly. + For streaming requests, synthesizes SSE chunks from the complete + response and signals EOF. + """ + is_streaming = intercept.get("stream", False) + chunk_queue: asyncio.Queue | None = intercept.get("chunk_queue") + future: asyncio.Future | None = intercept.get("response_future") + + if not is_streaming: + if future and not future.done(): + future.set_result(response_dict) + return + + if chunk_queue is None: + raise RuntimeError("chunk_queue missing on streaming intercept") + + choices = response_dict.get("choices") or [] + for choice in choices: + msg = choice.get("message") or {} + content_chunk = { + "id": response_dict.get("id", ""), + "object": "chat.completion.chunk", + "created": response_dict.get("created", int(time.time())), + "model": response_dict.get("model", ""), + "choices": [ + { + "index": choice.get("index", 0), + "delta": { + "role": "assistant", + "content": msg.get("content"), + "tool_calls": msg.get("tool_calls"), + }, + "finish_reason": None, + } + ], + } + await chunk_queue.put(content_chunk) + finish_chunk = { + "id": response_dict.get("id", ""), + "object": "chat.completion.chunk", + "created": response_dict.get("created", int(time.time())), + "model": response_dict.get("model", ""), + "choices": [ + { + "index": choice.get("index", 0), + "delta": {}, + "finish_reason": choice.get("finish_reason"), + } + ], + } + await chunk_queue.put(finish_chunk) + + await chunk_queue.put(None) + if future and not future.done(): + future.set_result(response_dict) + + +__all__ = [ + "InterceptionServer", + "deliver_response", +] From 171a3eaf23da0b0f109bd40af635b398ac10482c Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 15 May 2026 23:35:54 +0530 Subject: [PATCH 13/79] refactor: wire coding_agent_env with interception_gate --- envs/coding_agent_env/client.py | 7 +- envs/coding_agent_env/harness.py | 163 ++---------------- envs/coding_agent_env/models.py | 2 +- .../server/coding_environment.py | 68 +------- envs/coding_agent_env/server/gradio_ui.py | 6 +- 5 files changed, 26 insertions(+), 220 deletions(-) diff --git a/envs/coding_agent_env/client.py b/envs/coding_agent_env/client.py index 7e2a21696..c1e0f6f92 100644 --- a/envs/coding_agent_env/client.py +++ b/envs/coding_agent_env/client.py @@ -63,7 +63,7 @@ def run_rollout( verify: list[str] | None = None, # Bookkeeping / tunables task_id: str = "", - mode: str = "transparent_proxy", + mode: str = "black_box", disable_thinking: bool | None = None, max_tokens_cap: int = 4096, top_logprobs: int = 5, @@ -87,8 +87,9 @@ def run_rollout( Reward = ``passed_count / total`` unless any command writes a float to ``/home/user/logs/verifier/reward.txt`` (override). task_id: Echoed back in the result for traceability. - mode: ``"transparent_proxy"`` (captures per-token logprobs via - an in-sandbox FastAPI proxy) or ``"black_box"`` (no proxy). + mode: ``"black_box"`` (agent talks directly to the LLM) or + ``"interception_gate"`` (LLM calls routed to trainer-side + InterceptionServer for trainer-owned generation). disable_thinking: Inject ``chat_template_kwargs.enable_thinking=false`` on forwarded requests. Needed for Qwen3.5 vLLM; harmless on Instruct diff --git a/envs/coding_agent_env/harness.py b/envs/coding_agent_env/harness.py index ccbfa2cfc..295b07ac3 100644 --- a/envs/coding_agent_env/harness.py +++ b/envs/coding_agent_env/harness.py @@ -4,22 +4,10 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Coding-agent session factory + session — backed by CLIAgentDriver. - -This module exposes :class:`CodingAgentSession` and -:class:`CodingAgentSessionFactory` built on top of the generic -:class:`CLIAgentDriver` / :class:`CLIAgentSession` / -:class:`CLIAgentSessionFactory` from ``openenv.core.harness.agents``. - -Agent-specific (OpenCode spec) configuration (``opencode.json`` generation, provider -mapping, tool enable/disable) is handled by -:mod:`coding_agent_env.opencode_runtime` builders wired into the -:data:`OPENCODE_SPEC` via callable hooks. -""" +"""Coding-agent session factory + session — backed by CLIAgentDriver.""" from __future__ import annotations -from pathlib import Path from typing import Any, Literal from openenv.core.harness import ResourceSessionFactory @@ -28,8 +16,9 @@ CLIAgentSession, Verifier, ) +from openenv.core.harness.agents.interception_server import InterceptionServer from openenv.core.harness.agents.opencode import OPENCODE_SPEC -from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle +from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle from .config import CodingAgentConfig from .opencode_runtime import ( @@ -45,29 +34,7 @@ from .task import CodingAgentTask -# Inside-sandbox proxy paths (Mode B). -_PROXY_PORT = 7000 -_PROXY_TRACE_PATH = "/home/user/logs/agent/proxy_trace.jsonl" -_PROXY_LOG_PATH = "/home/user/logs/agent/proxy.log" - -_PROXY_SOURCE_PATH = ( - Path(__file__).resolve().parents[2] - / "src" - / "openenv" - / "core" - / "harness" - / "sandbox" - / "interception.py" -) - - class CodingAgentSession(CLIAgentSession): - """One live coding-agent rollout inside a sandbox. - - Extends :class:`CLIAgentSession` with Agent-specific (OpenCode spec) convenience - methods (``fetch_trace``, ``wait_for_completion`` with config-aware timeout). - """ - def __init__( self, *, @@ -76,9 +43,6 @@ def __init__( task: CodingAgentTask, verifier: Verifier | None = None, base_url_override: str | None = None, - proxy_trace_path: str | None = None, - proxy_bg_job: BgJob | None = None, - agent_bg_job: BgJob | None = None, ) -> None: super().__init__( spec=OPENCODE_SPEC, @@ -87,28 +51,18 @@ def __init__( config=config, verifier=verifier, base_url_override=base_url_override, - proxy_trace_path=proxy_trace_path, - proxy_bg_job=proxy_bg_job, - agent_bg_job=agent_bg_job, ) def fetch_trace(self) -> str: - """Return the raw ``opencode run`` log (JSONL when ``run_format=json``).""" return self.sandbox.read_text(agent_log_path(self.config)) def wait_for_completion(self, timeout_s: float | None = None) -> int: - """Block until the agent exits, returning its exit code.""" budget = timeout_s if timeout_s is not None else self.config.agent_timeout_s if self._agent_bg_job is None: - raise RuntimeError("Agent not started; call start_agent() first.") + raise RuntimeError("Agent not started.") return self._agent_bg_job.wait(timeout=budget) def start_agent(self) -> None: - """Launch ``opencode run`` as a background subprocess in the sandbox. - - The factory starts the agent during ``create()``; this method is a no-op - if the agent is already running. - """ if self._agent_bg_job is not None: return cmd = build_run_cmd(self.config) @@ -117,28 +71,19 @@ def start_agent(self) -> None: class CodingAgentSessionFactory(ResourceSessionFactory): - """Produce isolated per-rollout :class:`CodingAgentSession` instances. - - The factory owns sandbox provisioning, opencode install, config injection, - and (Mode B) proxy startup. Each :meth:`create` call returns a fresh - sandbox with a running agent. - - Internally delegates to :class:`CLIAgentDriver` for the generic - sandbox lifecycle (readiness probing, install retry, proxy startup). - Agent-specific (OpenCode spec) config generation uses ``opencode_runtime`` builders. - """ - def __init__( self, *, config: CodingAgentConfig, sandbox_backend: SandboxBackend, - mode: Literal["black_box", "transparent_proxy"] = "black_box", + mode: Literal["black_box", "interception_gate"] = "black_box", verifier: Verifier | None = None, install_timeout_s: int = 240, setup_timeout_s: int = 300, + interception_server: InterceptionServer | None = None, + interception_base_url: str | None = None, ) -> None: - if mode not in {"black_box", "transparent_proxy"}: + if mode not in {"black_box", "interception_gate"}: raise ValueError(f"Unknown mode: {mode!r}") self._config = config self._backend = sandbox_backend @@ -146,17 +91,14 @@ def __init__( self._verifier = verifier self._install_timeout_s = install_timeout_s self._setup_timeout_s = setup_timeout_s - - # Build a CLIAgentDriver for the shared lifecycle. self._driver = CLIAgentDriver( spec=OPENCODE_SPEC, sandbox_backend=sandbox_backend, mode=mode, install_timeout_s=install_timeout_s, setup_timeout_s=setup_timeout_s, - proxy_top_logprobs=config.proxy_top_logprobs, - proxy_max_tokens_cap=config.proxy_max_tokens_cap, - proxy_disable_thinking=config.proxy_disable_thinking, + interception_server=interception_server, + interception_base_url=interception_base_url, ) def create( @@ -168,87 +110,29 @@ def create( import logging _log = logging.getLogger(__name__) - oc_task = CodingAgentTask.coerce(task) sandbox_timeout = int(self._config.agent_timeout_s) + 300 - - _log.info( - "factory.create: creating sandbox timeout=%ds mode=%s", - sandbox_timeout, - self._mode, - ) sandbox = self._backend.create( timeout_s=sandbox_timeout, metadata={"episode_id": episode_id} if episode_id else None, ) - sid = getattr(sandbox, "sandbox_id", "?") - _log.info("factory.create: sandbox=%s — bootstrapping…", sid) - try: self._bootstrap_sandbox(sandbox, oc_task) except Exception as exc: _log.error("factory.create: bootstrap failed: %r", exc) sandbox.kill() raise - - base_url_override: str | None = None - proxy_trace_path: str | None = None - proxy_bg_job: BgJob | None = None - if self._mode == "transparent_proxy": - _log.info( - "factory.create: starting interception proxy on :%d → %s", - _PROXY_PORT, - self._config.base_url, - ) - proxy_bg_job, base_url_override, proxy_trace_path = ( - self._driver._start_proxy( - sandbox, - base_url=self._config.base_url, - api_key=self._config.api_key, - model=self._config.model, - ) - ) - _log.info("factory.create: proxy up at %s", base_url_override) - # Rewrite opencode.json so opencode points at the proxy. - proxy_cfg = CodingAgentConfig( - **{ - **self._config.model_dump(), - "provider": "openai_compatible", - "base_url": base_url_override, - } - ) - sandbox.write_text( - opencode_config_path(self._config), - build_opencode_json(proxy_cfg), - ) - session = CodingAgentSession( sandbox=sandbox, config=self._config, task=oc_task, verifier=self._verifier, - base_url_override=base_url_override, - proxy_trace_path=proxy_trace_path, - proxy_bg_job=proxy_bg_job, ) session.start_agent() return session - # ------------------------------------------------------------------ - # Bootstrap — delegates to CLIAgentDriver utilities - # ------------------------------------------------------------------ - - def _bootstrap_sandbox( - self, - sandbox: SandboxHandle, - task: CodingAgentTask, - ) -> None: - """Install opencode, write config + task files, run optional setup.""" - - # Stage 1: wait for the sandbox to be responsive. + def _bootstrap_sandbox(self, sandbox: SandboxHandle, task: CodingAgentTask) -> None: self._driver._wait_for_sandbox_ready(sandbox) - - # Stage 2: install opencode (skipped if pre-baked). if not self._driver._agent_already_installed(sandbox): self._driver._exec_with_retry( sandbox, @@ -258,24 +142,16 @@ def _bootstrap_sandbox( backoff_s=3.0, label="opencode install", ) - - # Stage 3: write opencode.json + task files. sandbox.write_text( - opencode_config_path(self._config), - build_opencode_json(self._config), + opencode_config_path(self._config), build_opencode_json(self._config) ) sandbox.write_text(instruction_path(self._config), task.instruction) - if self._config.system_prompt: sandbox.write_text( - system_prompt_path(self._config), - self._config.system_prompt, + system_prompt_path(self._config), self._config.system_prompt ) - for remote_path, content in task.upload_files.items(): sandbox.write_text(remote_path, content) - - # Stage 4: extra setup if self._config.extra_setup_shell: self._driver._exec_with_retry( sandbox, @@ -285,7 +161,6 @@ def _bootstrap_sandbox( backoff_s=2.0, label="extra_setup_shell", ) - if task.setup_shell: r = sandbox.exec(task.setup_shell, timeout=self._setup_timeout_s) if r.exit_code != 0: @@ -293,18 +168,6 @@ def _bootstrap_sandbox( f"task.setup_shell failed ({r.exit_code}): {r.stderr}" ) - def _start_proxy( - self, - sandbox: SandboxHandle, - ) -> tuple[BgJob, str, str]: - """Start proxy — delegates to driver.""" - return self._driver._start_proxy( - sandbox, - base_url=self._config.base_url, - api_key=self._config.api_key, - model=self._config.model, - ) - __all__ = [ "CodingAgentSession", diff --git a/envs/coding_agent_env/models.py b/envs/coding_agent_env/models.py index 3e31962fb..821b1bd57 100644 --- a/envs/coding_agent_env/models.py +++ b/envs/coding_agent_env/models.py @@ -59,7 +59,7 @@ class RolloutResult(BaseModel): reward: float | None = None agent_exit_code: int | None = None wall_s: float = 0.0 - mode: str = "transparent_proxy" + mode: str = "black_box" # Per-step results setup_results: list[CommandResult] = Field(default_factory=list) diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index e389eb759..0c8598cdd 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -143,7 +143,7 @@ def run_rollout( verify: Optional[list[str]] = None, # Bookkeeping / tunables task_id: str = "", - mode: str = "transparent_proxy", + mode: str = "black_box", disable_thinking: Optional[bool] = None, max_tokens_cap: int = 4096, top_logprobs: int = 5, @@ -359,10 +359,7 @@ def _emit(msg: str) -> None: ) session = factory.create(task=rollout_task) result.sandbox_id = session.sandbox.sandbox_id - _emit( - f"sandbox ready: {result.sandbox_id} — agent started " - f"({'proxy on :7000, logprobs capturing' if mode == 'transparent_proxy' else 'direct LLM, no logprobs'})" - ) + _emit(f"sandbox ready: {result.sandbox_id} — agent started (mode={mode})") # Run setup commands one at a time, *before* the agent starts. # The factory has already started the agent in start_agent() @@ -474,16 +471,9 @@ def _build_agent_config( api_key=api_key, model=model, agent_timeout_s=agent_timeout_s, - proxy_disable_thinking=disable_thinking, - proxy_top_logprobs=top_logprobs, - proxy_max_tokens_cap=max_tokens_cap if max_tokens_cap > 0 else None, ) - provider = ( - "openai" - if mode == "transparent_proxy" - else self._infer_pi_provider(base_url) - ) + provider = self._infer_pi_provider(base_url) return _GenericAgentConfig( base_url=base_url.rstrip("/"), api_key=api_key, @@ -524,9 +514,6 @@ def _build_session_factory( sandbox_backend=backend, mode=mode, verifier=None, - proxy_disable_thinking=disable_thinking, - proxy_top_logprobs=top_logprobs, - proxy_max_tokens_cap=max_tokens_cap if max_tokens_cap > 0 else None, ) @staticmethod @@ -602,53 +589,8 @@ def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: return files, extras def _collect_proxy_turns(self, session: Any) -> list[Any]: - turns: list[Any] = [] - - records: list[dict[str, Any]] = [] - if hasattr(session, "fetch_proxy_trace"): - try: - fetched = session.fetch_proxy_trace() - if isinstance(fetched, list): - records = [r for r in fetched if isinstance(r, dict)] - except Exception: - records = [] - - if not records: - proxy_trace_path = getattr(session, "_proxy_trace_path", None) - if not proxy_trace_path: - return turns - raw = self._safe_read(session.sandbox, proxy_trace_path) - for line in raw.splitlines(): - line = line.strip() - if not line: - continue - try: - rec = json.loads(line) - except Exception: - continue - if isinstance(rec, dict): - records.append(rec) - - for rec in records: - response = rec.get("response") or {} - turns.append( - self._RolloutTurn( - turn=int(rec.get("turn") or 0), - finish_reason=rec.get("finish_reason"), - completion_tokens=list(rec.get("completion_tokens") or []), - completion_token_ids=list(rec.get("completion_token_ids") or []), - per_token_logps=[ - float(x) - for x in (rec.get("per_token_logps") or []) - if x is not None - ], - latency_s=float(rec.get("latency_s") or 0.0), - timestamp=float(rec.get("timestamp") or 0.0), - upstream_status=response.get("upstream_status"), - upstream_error=response.get("upstream_error"), - ) - ) - return turns + """Logprob capture is now owned by the training loop via interception_gate.""" + return [] @staticmethod def _safe_read(sandbox: Any, path: str) -> str: diff --git a/envs/coding_agent_env/server/gradio_ui.py b/envs/coding_agent_env/server/gradio_ui.py index ef3f94aeb..82f130ce3 100644 --- a/envs/coding_agent_env/server/gradio_ui.py +++ b/envs/coding_agent_env/server/gradio_ui.py @@ -158,7 +158,7 @@ def _command_rows(items: list[dict[str, Any]]) -> list[list[str]]: def _logprobs_md(turns: list[dict[str, Any]]) -> str: if not turns: - return "_No proxy turns captured._\n\nThis is normal in `black_box` mode. In `transparent_proxy` mode, an empty list usually means the agent never made an LLM call (check the agent log)." + return "_No proxy turns captured._\n\nLogprob capture is handled by the training loop via `interception_gate` mode." n = len(turns) productive = sum(1 for t in turns if t.get("completion_tokens")) total_toks = sum(len(t.get("completion_tokens") or []) for t in turns) @@ -523,8 +523,8 @@ def apply_preset(name: str) -> tuple[str, str, str]: with gr.Accordion("Tunables", open=False): with gr.Row(): mode = gr.Dropdown( - choices=["transparent_proxy", "black_box"], - value="transparent_proxy", + choices=["black_box", "interception_gate"], + value="black_box", label="mode", ) disable_thinking = gr.Dropdown( From 52a024e2c616d4cb46c8dc03fb99fcc990201d5d Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 15 May 2026 23:36:04 +0530 Subject: [PATCH 14/79] chore: update tests for interception_gate, remove proxy test cases --- tests/core/test_cli_agent_driver.py | 81 ++----------------- tests/envs/test_coding_agent_env.py | 118 +++------------------------- 2 files changed, 16 insertions(+), 183 deletions(-) diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index 29bf06caa..0b218f19a 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -207,7 +207,6 @@ def test_cli_agent_spec_minimal(self): mcp_config=MCPConfigSpec(method="cli_flags"), ) assert spec.name == "test-agent" - assert spec.supports_logprob_proxy is True assert spec.default_timeout_s == 600.0 assert spec.setup is None assert spec.files is None @@ -229,7 +228,6 @@ def test_cli_agent_spec_full(self): mcp_config=MCPConfigSpec( method="config_file", path_template="{workdir}/mcp.json" ), - supports_logprob_proxy=True, default_timeout_s=900.0, setup="npm install -g full-agent", files={ @@ -457,41 +455,16 @@ def test_create_session_skips_install_when_prebaked(self): assert not any("apt-get install" in cmd for cmd in sbx.executed) session.close() - def test_create_session_with_proxy(self): + def test_create_session_interception_gate_requires_server(self): from openenv.core.harness.agents.cli_driver import CLIAgentDriver spec = _make_test_spec() - backend = FakeSandboxBackend() - driver = CLIAgentDriver( - spec=spec, - sandbox_backend=backend, - mode="transparent_proxy", - ) - - session = driver.create_session( - task=FakeTask(), - config=FakeConfig(), - ) - - sbx = backend.created[0] - - # Proxy source should have been uploaded - assert "/home/user/proxy/interception.py" in sbx.written - assert "/home/user/proxy/__init__.py" in sbx.written - - # Proxy should have been started as bg (before agent) - # and agent as second bg - assert len(sbx.bg_commands) == 2 - proxy_cmd, proxy_envs = sbx.bg_commands[0] - assert "interception.py" in proxy_cmd - assert proxy_envs == {"OPENCODE_UPSTREAM_API_KEY": "sk-test-key"} - - # Agent env should point at proxy - agent_cmd, agent_envs = sbx.bg_commands[1] - assert agent_envs is not None - assert agent_envs["BASE_URL"] == "http://127.0.0.1:7000/v1" - - session.close() + with pytest.raises(ValueError, match="InterceptionServer"): + CLIAgentDriver( + spec=spec, + sandbox_backend=FakeSandboxBackend(), + mode="interception_gate", + ) def test_create_session_uploads_task_files(self): from openenv.core.harness.agents.cli_driver import CLIAgentDriver @@ -679,49 +652,12 @@ def test_collect_artifacts_missing_required_raises(self): with pytest.raises(FileNotFoundError): session.collect_artifacts() - def test_fetch_proxy_trace_black_box(self): - from openenv.core.harness.agents.cli_driver import CLIAgentSession - - spec = _make_test_spec() - session = CLIAgentSession( - spec=spec, - sandbox=FakeSandbox(), - task=FakeTask(), - config=FakeConfig(), - proxy_trace_path=None, - ) - assert session.fetch_proxy_trace() == [] - - def test_fetch_proxy_trace_with_data(self): - from openenv.core.harness.agents.cli_driver import CLIAgentSession - - spec = _make_test_spec() - sbx = FakeSandbox() - trace_path = "/logs/proxy_trace.jsonl" - sbx.written[trace_path] = ( - json.dumps({"turn": 1, "latency_s": 0.5}) - + "\n" - + json.dumps({"turn": 2, "latency_s": 0.3}) - + "\n" - ) - session = CLIAgentSession( - spec=spec, - sandbox=sbx, - task=FakeTask(), - config=FakeConfig(), - proxy_trace_path=trace_path, - ) - trace = session.fetch_proxy_trace() - assert len(trace) == 2 - assert trace[0]["turn"] == 1 - def test_close_kills_sandbox_and_jobs(self): from openenv.core.harness.agents.cli_driver import CLIAgentSession spec = _make_test_spec() sbx = FakeSandbox() agent_job = FakeBgJob() - proxy_job = FakeBgJob() session = CLIAgentSession( spec=spec, @@ -729,12 +665,10 @@ def test_close_kills_sandbox_and_jobs(self): task=FakeTask(), config=FakeConfig(), agent_bg_job=agent_job, - proxy_bg_job=proxy_job, ) session.close() assert sbx._killed assert session._agent_bg_job is None - assert session._proxy_bg_job is None class TestCLIAgentSessionFactory: @@ -814,7 +748,6 @@ def test_spec_fields(self): "/home/user/.opencode/bin/opencode", "--version", ] - assert OPENCODE_SPEC.supports_logprob_proxy is True assert OPENCODE_SPEC.default_timeout_s == 900.0 assert OPENCODE_SPEC.mcp_config.method == "config_file" assert OPENCODE_SPEC.mcp_config.path_template is not None diff --git a/tests/envs/test_coding_agent_env.py b/tests/envs/test_coding_agent_env.py index 3a89a3ce6..6626c1c59 100644 --- a/tests/envs/test_coding_agent_env.py +++ b/tests/envs/test_coding_agent_env.py @@ -24,7 +24,6 @@ from __future__ import annotations import os -import shlex import sys import pytest @@ -172,7 +171,7 @@ def test_build_agent_config_opencode() -> None: env = CodingAgentEnvironment() cfg = env._build_agent_config( agent="opencode", - mode="transparent_proxy", + mode="black_box", base_url="https://api.openai.com/v1", api_key="sk-test", model="gpt-4o-mini", @@ -182,9 +181,8 @@ def test_build_agent_config_opencode() -> None: max_tokens_cap=2048, ) assert isinstance(cfg, env._CodingAgentConfig) - assert cfg.proxy_disable_thinking is True - assert cfg.proxy_top_logprobs == 7 - assert cfg.proxy_max_tokens_cap == 2048 + assert cfg.model == "gpt-4o-mini" + assert cfg.agent_timeout_s == 123.0 def test_build_agent_config_pi() -> None: @@ -206,9 +204,9 @@ def test_build_agent_config_pi() -> None: assert cfg.thinking == "off" assert cfg.model == "zai-org/GLM-5.1" - cfg_proxy = env._build_agent_config( + cfg_gate = env._build_agent_config( agent="pi", - mode="transparent_proxy", + mode="interception_gate", base_url="https://router.huggingface.co/v1", api_key="hf_xxx", model="zai-org/GLM-5.1", @@ -217,7 +215,7 @@ def test_build_agent_config_pi() -> None: top_logprobs=5, max_tokens_cap=4096, ) - assert cfg_proxy.provider == "openai" + assert cfg_gate.provider == "huggingface" # --------------------------------------------------------------------------- @@ -234,7 +232,7 @@ def test_rollout_result_serializes_round_trip() -> None: reward=0.75, agent_exit_code=0, wall_s=12.5, - mode="transparent_proxy", + mode="black_box", setup_results=[CommandResult(cmd="pip install pandas", exit_code=0)], verify_results=[CommandResult(cmd="pytest", exit_code=1, stderr="boom")], proxy_turns=[ @@ -288,105 +286,6 @@ def test_coding_agent_task_coerce_rejects_unknown_type() -> None: CodingAgentTask.coerce(42) # type: ignore[arg-type] -def test_start_proxy_keeps_upstream_key_out_of_command() -> None: - """The proxy API key must be passed via env, not shell argv.""" - from coding_agent_env import CodingAgentConfig, CodingAgentSessionFactory - - class FakeExecResult: - exit_code = 0 - stdout = "ok" - stderr = "" - - class FakeBgJob: - def wait(self, timeout: float | None = None) -> int: - return 0 - - def kill(self) -> None: - pass - - class FakeSandbox: - sandbox_id = "fake-sandbox" - - def __init__(self) -> None: - self.started_cmd: str | None = None - self.started_envs: dict[str, str] | None = None - self.written: dict[str, str] = {} - - def exec(self, *args, **kwargs) -> FakeExecResult: - return FakeExecResult() - - def start_bg(self, cmd: str, *, envs=None, cwd=None) -> FakeBgJob: - self.started_cmd = cmd - self.started_envs = envs - return FakeBgJob() - - def write_text(self, path: str, content: str) -> None: - self.written[path] = content - - def read_text(self, path: str) -> str: - return "" - - def exists(self, path: str) -> bool: - return path in self.written - - def kill(self) -> None: - pass - - secret = "sk-test '$(leak)" - model = "provider/model'; touch /tmp/pwn #" - config = CodingAgentConfig( - base_url="https://example.test/v1?x='y", - api_key=secret, - model=model, - ) - sandbox = FakeSandbox() - factory = CodingAgentSessionFactory( - config=config, - sandbox_backend=object(), # unused by this protected-method test - mode="transparent_proxy", - ) - - # _start_proxy delegates to CLIAgentDriver._start_proxy which runs the - # proxy inside the sandbox. The driver handles dep install + source upload. - factory._start_proxy(sandbox) - - assert sandbox.started_cmd is not None - assert sandbox.started_envs == {"OPENCODE_UPSTREAM_API_KEY": secret} - assert secret not in sandbox.started_cmd - assert "--upstream-api-key" not in sandbox.started_cmd - - argv = shlex.split(sandbox.started_cmd.split("&&", 1)[1].split(">", 1)[0].strip()) - assert argv[argv.index("--upstream-url") + 1] == config.base_url - assert argv[argv.index("--model-override") + 1] == model - - -def test_interception_cli_reads_upstream_key_from_env( - monkeypatch: pytest.MonkeyPatch, -) -> None: - from openenv.core.harness.sandbox import interception - - captured = {} - - def fake_serve(cfg) -> None: - captured["cfg"] = cfg - - monkeypatch.setattr(interception, "serve", fake_serve) - monkeypatch.setenv("OPENCODE_UPSTREAM_API_KEY", "sk-from-env") - monkeypatch.setattr( - sys, - "argv", - [ - "interception.py", - "--upstream-url", - "https://example.test/v1", - ], - ) - - interception.main() - - assert captured["cfg"].upstream_api_key == "sk-from-env" - - # --------------------------------------------------------------------------- # Integration — only runs when E2B + endpoint creds are present and the # user explicitly opts in via ``pytest -m integration``. @@ -447,7 +346,8 @@ async def _go() -> RolloutResult: assert result.reward == 1.0, ( f"expected reward=1.0 got {result.reward}: {result.error}" ) - assert result.proxy_turns, "expected at least one captured LLM turn" + # proxy_turns is now always empty — logprob capture is trainer-owned + # via interception_gate mode, not captured by the environment. assert any(f.endswith("/binary_search.py") for f in result.files), ( f"expected binary_search.py in workdir, got {list(result.files)}" ) From 4b1b707e749f900706b1d5677335030c15fb19c6 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 02:24:35 +0530 Subject: [PATCH 15/79] chore: address greptile review comments --- .../server/coding_environment.py | 50 ++++++++++--------- src/openenv/core/harness/agents/cli_driver.py | 2 - 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index 0c8598cdd..ceee49002 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -98,12 +98,14 @@ def __init__(self) -> None: from openenv.core.harness.agents import get_agent_spec from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory - from coding_agent_env import ( - E2BSandboxBackend, - CodingAgentConfig, - CodingAgentSessionFactory, - CodingAgentTask, - ) + from coding_agent_env.config import CodingAgentConfig + from coding_agent_env.harness import CodingAgentSessionFactory + from coding_agent_env.task import CodingAgentTask + + try: + from openenv.core.harness.sandbox import E2BSandboxBackend + except ImportError: + E2BSandboxBackend = None # type: ignore[assignment,misc] self._CommandResult = CommandResult self._RolloutResult = RolloutResult @@ -330,14 +332,18 @@ def _emit(msg: str) -> None: max_tokens_cap=max_tokens_cap, ) - # Concatenate setup commands into a single ``set -e`` script and let - # the primitive run it as ``task.setup_shell`` before the agent - # starts. The per-command tracking happens here too — we re-run - # each command in a wrapper that captures exit/stdout/stderr. - # That way the primitive still aborts on setup failure AND we get - # observability in the response. + # Concatenate setup commands into a single ``set -e`` script so the + # primitive runs them inside _bootstrap_sandbox BEFORE the agent + # starts. This avoids the race where the agent's first tool call + # depends on files or packages that setup is still installing. + setup_shell: str | None = None + if setup: + # ``set -e`` makes the script abort on the first failing command. + setup_shell = "set -e\n" + "\n".join(setup) + rollout_task = self._CodingAgentTask( instruction=instruction, + setup_shell=setup_shell, metadata={"task_id": task_id, "agent": agent}, ) @@ -361,23 +367,21 @@ def _emit(msg: str) -> None: result.sandbox_id = session.sandbox.sandbox_id _emit(f"sandbox ready: {result.sandbox_id} — agent started (mode={mode})") - # Run setup commands one at a time, *before* the agent starts. - # The factory has already started the agent in start_agent() - # during create(); to keep the order "setup → agent → verify" - # we'd need to restructure. As a pragmatic compromise we run - # setup IMMEDIATELY after create(), which races with the agent - # for ~1-2s but is fine for typical pip/git/download work - # because most agent CLIs take a while before their first model - # call. + # Re-run setup commands individually for per-command + # observability in the response. The commands already ran + # atomically via setup_shell above, so these re-runs are + # idempotent — they exist only to populate + # result.setup_results with per-command exit/stdout/stderr. for i, cmd in enumerate(setup, 1): - _emit(f"setup [{i}/{len(setup)}]: {cmd[:80]}") cr = self._exec_command(session.sandbox, cmd) result.setup_results.append(cr) if cr.exit_code != 0: + # Should not happen — setup_shell already succeeded + # during bootstrap, but record it for diagnostics. result.error = ( - f"setup command failed (exit {cr.exit_code}): {cmd[:120]}" + f"setup replay failed (exit {cr.exit_code}): {cmd[:120]}" ) - _emit(f"setup FAILED at [{i}]: exit={cr.exit_code}") + _emit(f"setup replay FAILED at [{i}]: exit={cr.exit_code}") break # Block until the agent is done (or setup already failed). diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 0e58af9e0..1d934777d 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -458,8 +458,6 @@ def _exec_with_retry( last_stdout = r.stdout or "" last_stderr = r.stderr or "" last_exit = r.exit_code - if last_stderr.strip(): - break except Exception as exc: last_stderr = f"{type(exc).__name__}: {exc}" last_exit = -1 From 8830b72caf9140c94f04adfa1343778fda7cded3 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 03:00:31 +0530 Subject: [PATCH 16/79] feat: task adapter + mini subset builder --- envs/mini_swe_env/__init__.py | 31 ++ .../mini_swe_env/task_loader_swebench_lite.py | 515 ++++++++++++++++++ .../mini_swe_env/tasks/mini_swe_eval.jsonl | 4 + .../mini_swe_env/tasks/mini_swe_train.jsonl | 16 + scripts/swe/build_mini_subset.py | 223 ++++++++ tests/envs/test_swe_task_loader.py | 136 +++++ 6 files changed, 925 insertions(+) create mode 100644 envs/mini_swe_env/__init__.py create mode 100644 envs/mini_swe_env/task_loader_swebench_lite.py create mode 100644 examples/mini_swe_env/tasks/mini_swe_eval.jsonl create mode 100644 examples/mini_swe_env/tasks/mini_swe_train.jsonl create mode 100644 scripts/swe/build_mini_subset.py create mode 100644 tests/envs/test_swe_task_loader.py diff --git a/envs/mini_swe_env/__init__.py b/envs/mini_swe_env/__init__.py new file mode 100644 index 000000000..7b604d3f7 --- /dev/null +++ b/envs/mini_swe_env/__init__.py @@ -0,0 +1,31 @@ +"""SWE environment package task adapter surface.""" + +from .task_loader_swebench_lite import ( + adapt_swebench_lite_row, + adapt_swebench_lite_rows, + AdaptationSkip, + coerce_swe_task, + deterministic_train_eval_split, + load_task_file, + read_jsonl_rows, + SWEBenchLiteAdapterError, + SWETask, + SWETaskValidationError, + validate_swe_task, + write_tasks_jsonl, +) + +__all__ = [ + "AdaptationSkip", + "SWEBenchLiteAdapterError", + "SWETask", + "SWETaskValidationError", + "adapt_swebench_lite_row", + "adapt_swebench_lite_rows", + "coerce_swe_task", + "deterministic_train_eval_split", + "load_task_file", + "read_jsonl_rows", + "validate_swe_task", + "write_tasks_jsonl", +] diff --git a/envs/mini_swe_env/task_loader_swebench_lite.py b/envs/mini_swe_env/task_loader_swebench_lite.py new file mode 100644 index 000000000..532339007 --- /dev/null +++ b/envs/mini_swe_env/task_loader_swebench_lite.py @@ -0,0 +1,515 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Task adapter utilities for SWE-bench Lite. + +Includes: + - normalize dataset rows into ``SWETask`` + - deterministic mini-subset selection and train/eval split + - explicit schema validation and skip reasons +""" + +from __future__ import annotations + +import hashlib +import json +import re +import shlex +from dataclasses import asdict, dataclass, field +from pathlib import Path +from typing import Any, Iterable, Sequence + +SOURCE_NAME = "swebench_lite" +DEFAULT_TIMEOUT_S = 1800 + + +class SWETaskValidationError(ValueError): + """Raised when a task fails schema validation.""" + + +class SWEBenchLiteAdapterError(ValueError): + """Raised when a SWE-bench Lite row cannot be adapted.""" + + +@dataclass(frozen=True) +class SWETask: + task_id: str + source: str + instance_id: str + repo: str + base_commit: str + instruction: str + setup: list[str] + verify: list[str] + timeout_s: int = DEFAULT_TIMEOUT_S + sandbox_image: str | None = None + metadata: dict[str, Any] = field(default_factory=dict) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a JSONL-friendly dictionary.""" + return asdict(self) + + +@dataclass(frozen=True) +class AdaptationSkip: + row_index: int + reason: str + instance_id: str | None = None + + +def validate_swe_task(task: SWETask) -> None: + """Validate a ``SWETask`` and raise explicit schema errors.""" + errors: list[str] = [] + + for field_name in ( + "task_id", + "source", + "instance_id", + "repo", + "base_commit", + "instruction", + ): + value = getattr(task, field_name) + if not isinstance(value, str) or not value.strip(): + errors.append(f"{field_name} must be a non-empty string") + + if not isinstance(task.setup, list): + errors.append("setup must be a list[str]") + else: + for idx, command in enumerate(task.setup): + if not isinstance(command, str) or not command.strip(): + errors.append(f"setup[{idx}] must be a non-empty string") + + if not isinstance(task.verify, list): + errors.append("verify must be a list[str]") + elif not task.verify: + errors.append("verify must contain at least one command") + else: + for idx, command in enumerate(task.verify): + if not isinstance(command, str) or not command.strip(): + errors.append(f"verify[{idx}] must be a non-empty string") + + if not isinstance(task.timeout_s, int) or task.timeout_s <= 0: + errors.append("timeout_s must be a positive int") + + if task.sandbox_image is not None and ( + not isinstance(task.sandbox_image, str) or not task.sandbox_image.strip() + ): + errors.append("sandbox_image must be None or a non-empty string") + + if not isinstance(task.metadata, dict): + errors.append("metadata must be a dict") + + if errors: + raise SWETaskValidationError("; ".join(errors)) + + +def coerce_swe_task(value: SWETask | dict[str, Any]) -> SWETask: + """Coerce an input mapping into a validated ``SWETask``.""" + if isinstance(value, SWETask): + validate_swe_task(value) + return value + if not isinstance(value, dict): + raise SWETaskValidationError( + f"Expected SWETask or dict, got {type(value).__name__}" + ) + task = SWETask(**value) + validate_swe_task(task) + return task + + +def adapt_swebench_lite_row(row: dict[str, Any]) -> SWETask: + """Convert one SWE-bench Lite row into the internal ``SWETask`` shape.""" + if not isinstance(row, dict): + raise SWEBenchLiteAdapterError(f"row must be a dict, got {type(row).__name__}") + + instance_id = _pick_str(row, "instance_id", required=True) + repo = _pick_str(row, "repo", "repository", required=True) + base_commit = _pick_str( + row, + "base_commit", + "commit", + "base_sha", + "environment_setup_commit", + required=True, + ) + instruction = _pick_str( + row, + "instruction", + "problem_statement", + "prompt", + required=True, + ) + + setup = _pick_commands( + row, + "setup", + "setup_commands", + "setup_script", + "setup_scripts", + ) + verify = _pick_commands( + row, + "verify", + "verify_commands", + "evaluation_commands", + "test_commands", + ) + + if not verify: + verify = _derive_verify_from_test_lists(row) + + timeout_s = _coerce_positive_int(row.get("timeout_s"), default=DEFAULT_TIMEOUT_S) + sandbox_image = _pick_optional_str(row, "sandbox_image", "image") + + task_id = _pick_optional_str(row, "task_id") or f"{SOURCE_NAME}::{instance_id}" + + fail_to_pass = _parse_json_string_list( + row.get("FAIL_TO_PASS", row.get("fail_to_pass")) + ) + pass_to_pass = _parse_json_string_list( + row.get("PASS_TO_PASS", row.get("pass_to_pass")) + ) + hints_text = _pick_optional_str(row, "hints_text") + + metadata: dict[str, Any] = { + "dataset": SOURCE_NAME, + "created_at": _pick_optional_str(row, "created_at"), + "version": _pick_optional_str(row, "version"), + "fail_to_pass_count": len(fail_to_pass), + "pass_to_pass_count": len(pass_to_pass), + } + if hints_text: + # Keep metadata compact for JSONL task files. + metadata["hints_preview"] = hints_text[:280] + metadata = {k: v for k, v in metadata.items() if v not in (None, "")} + + task = SWETask( + task_id=task_id, + source=SOURCE_NAME, + instance_id=instance_id, + repo=repo, + base_commit=base_commit, + instruction=instruction, + setup=setup, + verify=verify, + timeout_s=timeout_s, + sandbox_image=sandbox_image, + metadata=metadata, + ) + + try: + validate_swe_task(task) + except SWETaskValidationError as exc: + raise SWEBenchLiteAdapterError( + f"row {instance_id!r} failed schema validation: {exc}" + ) from exc + + return task + + +def adapt_swebench_lite_rows( + rows: Iterable[dict[str, Any]], *, strict: bool = False +) -> tuple[list[SWETask], list[AdaptationSkip]]: + """Adapt an iterable of rows. + + When ``strict=False`` invalid rows are skipped and reported in ``AdaptationSkip``. + When ``strict=True`` the first adapter error is raised. + """ + tasks: list[SWETask] = [] + skipped: list[AdaptationSkip] = [] + + for index, row in enumerate(rows, start=1): + try: + task = adapt_swebench_lite_row(row) + tasks.append(task) + except SWEBenchLiteAdapterError as exc: + if strict: + raise + instance_id = None + if isinstance(row, dict): + maybe_id = row.get("instance_id") + instance_id = str(maybe_id) if maybe_id else None + skipped.append( + AdaptationSkip( + row_index=index, reason=str(exc), instance_id=instance_id + ) + ) + + return tasks, skipped + + +def read_jsonl_rows(path: str | Path) -> list[dict[str, Any]]: + """Read newline-delimited JSON rows from ``path``.""" + file_path = Path(path) + rows: list[dict[str, Any]] = [] + for line_no, raw_line in enumerate(file_path.read_text().splitlines(), start=1): + line = raw_line.strip() + if not line: + continue + try: + payload = json.loads(line) + except json.JSONDecodeError as exc: + raise SWEBenchLiteAdapterError( + f"invalid JSON on line {line_no}: {exc.msg}" + ) from exc + if not isinstance(payload, dict): + raise SWEBenchLiteAdapterError( + f"line {line_no} must decode to an object, got {type(payload).__name__}" + ) + rows.append(payload) + return rows + + +def write_tasks_jsonl(path: str | Path, tasks: Sequence[SWETask]) -> None: + """Write validated tasks to JSONL.""" + out_path = Path(path) + out_path.parent.mkdir(parents=True, exist_ok=True) + + lines: list[str] = [] + for task in tasks: + validate_swe_task(task) + lines.append(json.dumps(task.to_dict(), sort_keys=True)) + out_path.write_text("\n".join(lines) + ("\n" if lines else "")) + + +def deterministic_train_eval_split( + tasks: Sequence[SWETask], + *, + subset_size: int = 20, + train_size: int = 16, + seed: int = 17, +) -> tuple[list[SWETask], list[SWETask]]: + """Create a deterministic subset and split without relying on RNG internals.""" + if subset_size <= 0: + raise ValueError("subset_size must be > 0") + if train_size <= 0: + raise ValueError("train_size must be > 0") + if train_size >= subset_size: + raise ValueError("train_size must be < subset_size") + + deduped: dict[str, SWETask] = {} + for task in tasks: + validate_swe_task(task) + deduped[task.task_id] = task + + unique_tasks = list(deduped.values()) + if len(unique_tasks) < subset_size: + raise ValueError( + f"need at least {subset_size} valid unique tasks, got {len(unique_tasks)}" + ) + + ranked = sorted( + unique_tasks, + key=lambda task: (_stable_seeded_rank(task.task_id, seed), task.task_id), + ) + selected = ranked[:subset_size] + return selected[:train_size], selected[train_size:] + + +def load_task_file(path: str | Path) -> list[SWETask]: + """Load and validate existing task JSONL file.""" + return [coerce_swe_task(row) for row in read_jsonl_rows(path)] + + +def _stable_seeded_rank(task_id: str, seed: int) -> str: + digest = hashlib.sha256(f"{seed}:{task_id}".encode("utf-8")).hexdigest() + return digest + + +def _pick_str(row: dict[str, Any], *keys: str, required: bool = False) -> str: + value = _pick_optional_str(row, *keys) + if value is not None: + return value + if required: + key_list = ", ".join(keys) + raise SWEBenchLiteAdapterError(f"missing required field(s): {key_list}") + return "" + + +def _pick_optional_str(row: dict[str, Any], *keys: str) -> str | None: + for key in keys: + if key not in row: + continue + value = row.get(key) + if value is None: + continue + if isinstance(value, str): + normalized = value.strip() + if normalized: + return normalized + continue + normalized = str(value).strip() + if normalized: + return normalized + return None + + +def _coerce_positive_int(value: Any, *, default: int) -> int: + if value is None: + return default + try: + parsed = int(value) + except Exception as exc: + raise SWEBenchLiteAdapterError( + f"timeout_s must be an int, got {value!r}" + ) from exc + if parsed <= 0: + raise SWEBenchLiteAdapterError("timeout_s must be > 0") + return parsed + + +def _pick_commands(row: dict[str, Any], *keys: str) -> list[str]: + for key in keys: + if key in row: + return _coerce_commands(row.get(key), field_name=key) + return [] + + +def _coerce_commands(value: Any, *, field_name: str) -> list[str]: + if value is None: + return [] + + if isinstance(value, str): + stripped = value.strip() + if not stripped: + return [] + if stripped.startswith("[") and stripped.endswith("]"): + try: + parsed = json.loads(stripped) + except json.JSONDecodeError: + parsed = None + else: + return _coerce_commands(parsed, field_name=field_name) + return [line.strip() for line in stripped.splitlines() if line.strip()] + + if isinstance(value, Sequence) and not isinstance( + value, (bytes, bytearray, memoryview) + ): + commands: list[str] = [] + for item in value: + if item is None: + continue + if not isinstance(item, str): + item = str(item) + normalized = item.strip() + if not normalized: + continue + commands.append(normalized) + return commands + + raise SWEBenchLiteAdapterError( + f"{field_name} must be a string or list of strings, got {type(value).__name__}" + ) + + +def _derive_verify_from_test_lists(row: dict[str, Any]) -> list[str]: + candidates = _parse_json_string_list( + row.get("FAIL_TO_PASS", row.get("fail_to_pass")) + ) + if not candidates: + candidates = _parse_json_string_list( + row.get("PASS_TO_PASS", row.get("pass_to_pass")) + ) + + commands: list[str] = [] + for item in candidates: + normalized = str(item).strip() + if not normalized: + continue + if _looks_like_shell_command(normalized): + commands.append(normalized) + continue + + converted_unittest_name = _convert_unittest_style_test_name(normalized) + if converted_unittest_name is not None: + commands.append( + f"python -m pytest -q {shlex.quote(converted_unittest_name)}" + ) + continue + + if _looks_like_pytest_nodeid(normalized): + commands.append(f"python -m pytest -q {shlex.quote(normalized)}") + else: + commands.append(f"python -m pytest -q -k {shlex.quote(normalized)}") + return commands + + +def _parse_json_string_list(value: Any) -> list[str]: + if value is None: + return [] + if isinstance(value, list): + return [str(item) for item in value if str(item).strip()] + if isinstance(value, str): + stripped = value.strip() + if not stripped: + return [] + try: + parsed = json.loads(stripped) + except json.JSONDecodeError: + return [stripped] + if isinstance(parsed, list): + return [str(item) for item in parsed if str(item).strip()] + return [str(parsed)] + return [str(value)] + + +def _looks_like_shell_command(value: str) -> bool: + starts = ( + "pytest ", + "python ", + "python3 ", + "tox ", + "nox ", + "bash ", + "./", + "sh ", + ) + if value.startswith(starts): + return True + return any(token in value for token in (" && ", ";", "|", " > ", " < ")) + + +def _looks_like_pytest_nodeid(value: str) -> bool: + return "::" in value or value.endswith(".py") or "/" in value + + +def _convert_unittest_style_test_name(value: str) -> str | None: + # Example: "test_name (pkg.subpkg.module.TestClass)" + match = re.match(r"^([^\s(]+)\s+\(([^()]+)\)$", value) + if not match: + return None + + test_name = match.group(1).strip() + location = match.group(2).strip() + if not test_name or not location: + return None + + parts = [part for part in location.split(".") if part] + if len(parts) < 2: + return None + + class_name = parts[-1] + module_path = "/".join(parts[:-1]) + ".py" + return f"{module_path}::{class_name}::{test_name}" + + +__all__ = [ + "AdaptationSkip", + "DEFAULT_TIMEOUT_S", + "SOURCE_NAME", + "SWEBenchLiteAdapterError", + "SWETask", + "SWETaskValidationError", + "adapt_swebench_lite_row", + "adapt_swebench_lite_rows", + "coerce_swe_task", + "deterministic_train_eval_split", + "load_task_file", + "read_jsonl_rows", + "validate_swe_task", + "write_tasks_jsonl", +] diff --git a/examples/mini_swe_env/tasks/mini_swe_eval.jsonl b/examples/mini_swe_env/tasks/mini_swe_eval.jsonl new file mode 100644 index 000000000..ff77845b1 --- /dev/null +++ b/examples/mini_swe_env/tasks/mini_swe_eval.jsonl @@ -0,0 +1,4 @@ +{"base_commit": "3ed7590ed411bd93b26098faab4f23619cdb2267", "instance_id": "sphinx-doc__sphinx-8713", "instruction": "napoleon_use_param should also affect \"other parameters\" section\nSubject: napoleon_use_param should also affect \"other parameters\" section\r\n\r\n### Problem\r\nCurrently, napoleon always renders the Other parameters section as if napoleon_use_param was False, see source\r\n```\r\n def _parse_other_parameters_section(self, section):\r\n # type: (unicode) -> List[unicode]\r\n return self._format_fields(_('Other Parameters'), self._consume_fields())\r\n\r\n def _parse_parameters_section(self, section):\r\n # type: (unicode) -> List[unicode]\r\n fields = self._consume_fields()\r\n if self._config.napoleon_use_param:\r\n return self._format_docutils_params(fields)\r\n else:\r\n return self._format_fields(_('Parameters'), fields)\r\n```\r\nwhereas it would make sense that this section should follow the same formatting rules as the Parameters section.\r\n\r\n#### Procedure to reproduce the problem\r\n```\r\nIn [5]: print(str(sphinx.ext.napoleon.NumpyDocstring(\"\"\"\\ \r\n ...: Parameters \r\n ...: ---------- \r\n ...: x : int \r\n ...: \r\n ...: Other parameters \r\n ...: ---------------- \r\n ...: y: float \r\n ...: \"\"\"))) \r\n:param x:\r\n:type x: int\r\n\r\n:Other Parameters: **y** (*float*)\r\n```\r\n\r\nNote the difference in rendering.\r\n\r\n#### Error logs / results\r\nSee above.\r\n\r\n#### Expected results\r\n```\r\n:param x:\r\n:type x: int\r\n\r\n:Other Parameters: // Or some other kind of heading.\r\n:param: y\r\n:type y: float\r\n```\r\n\r\nAlternatively another separate config value could be introduced, but that seems a bit overkill.\r\n\r\n### Reproducible project / your project\r\nN/A\r\n\r\n### Environment info\r\n- OS: Linux\r\n- Python version: 3.7\r\n- Sphinx version: 1.8.1", "metadata": {"created_at": "2021-01-20T14:24:12Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 45, "version": "4.0"}, "repo": "sphinx-doc/sphinx", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sphinx-doc__sphinx-8713", "timeout_s": 1800, "verify": ["python -m pytest -q tests/test_ext_napoleon_docstring.py::NumpyDocstringTest::test_parameters_with_class_reference"]} +{"base_commit": "d232fd76a85870daf345fd8f8d617fe7802ae194", "instance_id": "django__django-11910", "instruction": "ForeignKey's to_field parameter gets the old field's name when renaming a PrimaryKey.\nDescription\n\t\nHaving these two models \nclass ModelA(models.Model):\n\tfield_wrong = models.CharField('field1', max_length=50, primary_key=True) # I'm a Primary key.\nclass ModelB(models.Model):\n\tfield_fk = models.ForeignKey(ModelA, blank=True, null=True, on_delete=models.CASCADE) \n... migrations applyed ...\nthe ModelA.field_wrong field has been renamed ... and Django recognizes the \"renaming\"\n# Primary key renamed\nclass ModelA(models.Model):\n\tfield_fixed = models.CharField('field1', max_length=50, primary_key=True) # I'm a Primary key.\nAttempts to to_field parameter. \nThe to_field points to the old_name (field_typo) and not to the new one (\"field_fixed\")\nclass Migration(migrations.Migration):\n\tdependencies = [\n\t\t('app1', '0001_initial'),\n\t]\n\toperations = [\n\t\tmigrations.RenameField(\n\t\t\tmodel_name='modela',\n\t\t\told_name='field_wrong',\n\t\t\tnew_name='field_fixed',\n\t\t),\n\t\tmigrations.AlterField(\n\t\t\tmodel_name='modelb',\n\t\t\tname='modela',\n\t\t\tfield=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='app1.ModelB', to_field='field_wrong'),\n\t\t),\n\t]", "metadata": {"created_at": "2019-10-14T01:56:49Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Thanks for this ticket. It looks like a regression in dcdd219ee1e062dc6189f382e0298e0adf5d5ddf, because an AlterField operation wasn't generated in such cases before this change (and I don't think we need it).", "pass_to_pass_count": 110, "version": "3.1"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-11910", "timeout_s": 1800, "verify": ["python -m pytest -q migrations/test_autodetector.py::AutodetectorTests::test_rename_referenced_primary_key"]} +{"base_commit": "06632c0d185128a53c57ccc73b25b6408e90bb89", "instance_id": "scikit-learn__scikit-learn-14983", "instruction": "RepeatedKFold and RepeatedStratifiedKFold do not show correct __repr__ string\n#### Description\r\n\r\n`RepeatedKFold` and `RepeatedStratifiedKFold` do not show correct \\_\\_repr\\_\\_ string.\r\n\r\n#### Steps/Code to Reproduce\r\n\r\n```python\r\n>>> from sklearn.model_selection import RepeatedKFold, RepeatedStratifiedKFold\r\n>>> repr(RepeatedKFold())\r\n>>> repr(RepeatedStratifiedKFold())\r\n```\r\n\r\n#### Expected Results\r\n\r\n```python\r\n>>> repr(RepeatedKFold())\r\nRepeatedKFold(n_splits=5, n_repeats=10, random_state=None)\r\n>>> repr(RepeatedStratifiedKFold())\r\nRepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=None)\r\n```\r\n\r\n#### Actual Results\r\n\r\n```python\r\n>>> repr(RepeatedKFold())\r\n''\r\n>>> repr(RepeatedStratifiedKFold())\r\n''\r\n```\r\n\r\n#### Versions\r\n```\r\nSystem:\r\n python: 3.7.4 (default, Aug 9 2019, 18:34:13) [MSC v.1915 64 bit (AMD64)]\r\nexecutable: D:\\anaconda3\\envs\\xyz\\python.exe\r\n machine: Windows-10-10.0.16299-SP0\r\n\r\nBLAS:\r\n macros:\r\n lib_dirs:\r\ncblas_libs: cblas\r\n\r\nPython deps:\r\n pip: 19.2.2\r\nsetuptools: 41.0.1\r\n sklearn: 0.21.2\r\n numpy: 1.16.4\r\n scipy: 1.3.1\r\n Cython: None\r\n pandas: 0.24.2\r\n```", "metadata": {"created_at": "2019-09-14T15:31:18Z", "dataset": "swebench_lite", "fail_to_pass_count": 2, "hints_preview": "The `__repr__` is not defined in the `_RepeatedSplit` class from which these cross-validation are inheriting. A possible fix should be:\r\n\r\n```diff\r\ndiff --git a/sklearn/model_selection/_split.py b/sklearn/model_selection/_split.py\r\nindex ab681e89c..8a16f68bc 100644\r\n--- a/sklearn", "pass_to_pass_count": 105, "version": "0.22"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-14983", "timeout_s": 1800, "verify": ["python -m pytest -q 'sklearn/model_selection/tests/test_split.py::test_repeated_cv_repr[RepeatedKFold]'", "python -m pytest -q 'sklearn/model_selection/tests/test_split.py::test_repeated_cv_repr[RepeatedStratifiedKFold]'"]} +{"base_commit": "d3b4158dea271485e3daa11bf82e69b8dab348ce", "instance_id": "sympy__sympy-24909", "instruction": "Bug with milli prefix\nWhat happened:\r\n```\r\nIn [1]: from sympy.physics.units import milli, W\r\nIn [2]: milli*W == 1\r\nOut[2]: True\r\nIn [3]: W*milli\r\nOut[3]: watt*Prefix(milli, m, -3, 10)\r\n```\r\nWhat I expected to happen: milli*W should evaluate to milli watts / mW\r\n\r\n`milli*W` or more generally `milli` times some unit evaluates to the number 1. I have tried this with Watts and Volts, I'm not sure what other cases this happens. I'm using sympy version 1.11.1-1 on Arch Linux with Python 3.10.9. If you cannot reproduce I would be happy to be of any assitance.", "metadata": {"created_at": "2023-03-13T14:24:25Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "I get a 1 for all of the following (and some are redundant like \"V\" and \"volt\"):\r\n```python\r\nW, joule, ohm, newton, volt, V, v, volts, henrys, pa, kilogram, ohms, kilograms, Pa, weber, tesla, Wb, H, wb, newtons, kilometers, webers, pascals, kilometer, watt, T, km, kg, joules, pas", "pass_to_pass_count": 2, "version": "1.13"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-24909", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_prefix_operations"]} diff --git a/examples/mini_swe_env/tasks/mini_swe_train.jsonl b/examples/mini_swe_env/tasks/mini_swe_train.jsonl new file mode 100644 index 000000000..7a4f010f7 --- /dev/null +++ b/examples/mini_swe_env/tasks/mini_swe_train.jsonl @@ -0,0 +1,16 @@ +{"base_commit": "5c2e1f96a7ff562d4a778f4ca9ffc9c81557197e", "instance_id": "sympy__sympy-11870", "instruction": "simplifying exponential -> trig identities\n```\r\nf = 1 / 2 * (-I*exp(I*k) + I*exp(-I*k))\r\ntrigsimp(f)\r\n```\r\n\r\nIdeally, this would yield `sin(k)`. Is there a way to do this?\r\n\r\nAs a corollary, it would be awesome if \r\n\r\n```\r\nf = 1 / 2 / k* (-I*exp(I*k) + I*exp(-I*k))\r\ntrigsimp(f)\r\n```\r\n\r\ncould yield `sinc(k)`. Thank you for your consideration!", "metadata": {"created_at": "2016-11-17T21:36:03Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "rewrite can be used:\n\n```\n>>> f = S(1) / 2 * (-I*exp(I*k) + I*exp(-I*k))\n>>> f.rewrite(sin).simplify()\nsin(k)\n```\n\nThank you for that suggestion!\n\n> On Nov 17, 2016, at 01:06, Kalevi Suominen notifications@github.com wrote:\n> \n> rewrite can be used:\n> \n> > > > f = S(1) / 2 \\* (-I", "pass_to_pass_count": 58, "version": "1.1"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-11870", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_sinc"]} +{"base_commit": "4964b468c83c06971eb743fbc57cc404f760c573", "instance_id": "pytest-dev__pytest-8365", "instruction": "tmpdir creation fails when the username contains illegal characters for directory names\n`tmpdir`, `tmpdir_factory` and `tmp_path_factory` rely on `getpass.getuser()` for determining the `basetemp` directory. I found that the user name returned by `getpass.getuser()` may return characters that are not allowed for directory names. This may lead to errors while creating the temporary directory.\r\n\r\nThe situation in which I reproduced this issue was while being logged in through an ssh connection into my Windows 10 x64 Enterprise version (1909) using an OpenSSH_for_Windows_7.7p1 server. In this configuration the command `python -c \"import getpass; print(getpass.getuser())\"` returns my domain username e.g. `contoso\\john_doe` instead of `john_doe` as when logged in regularly using a local session.\r\n\r\nWhen trying to create a temp directory in pytest through e.g. `tmpdir_factory.mktemp('foobar')` this fails with the following error message:\r\n```\r\nself = WindowsPath('C:/Users/john_doe/AppData/Local/Temp/pytest-of-contoso/john_doe')\r\nmode = 511, parents = False, exist_ok = True\r\n\r\n def mkdir(self, mode=0o777, parents=False, exist_ok=False):\r\n \"\"\"\r\n Create a new directory at this given path.\r\n \"\"\"\r\n if self._closed:\r\n self._raise_closed()\r\n try:\r\n> self._accessor.mkdir(self, mode)\r\nE FileNotFoundError: [WinError 3] The system cannot find the path specified: 'C:\\\\Users\\\\john_doe\\\\AppData\\\\Local\\\\Temp\\\\pytest-of-contoso\\\\john_doe'\r\n\r\nC:\\Python38\\lib\\pathlib.py:1266: FileNotFoundError\r\n```\r\n\r\nI could also reproduce this without the complicated ssh/windows setup with pytest 6.2.2 using the following commands from a `cmd`:\r\n```bat\r\necho def test_tmpdir(tmpdir):>test_tmp.py\r\necho pass>>test_tmp.py\r\nset LOGNAME=contoso\\john_doe\r\npy.test test_tmp.py\r\n```\r\n\r\nThanks for having a look at this!", "metadata": {"created_at": "2021-02-22T20:26:35Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Thanks for the report @pborsutzki!", "pass_to_pass_count": 32, "version": "6.3"}, "repo": "pytest-dev/pytest", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::pytest-dev__pytest-8365", "timeout_s": 1800, "verify": ["python -m pytest -q testing/test_tmpdir.py::test_tmp_path_factory_handles_invalid_dir_characters"]} +{"base_commit": "4652f1f0aa459a7b980441d629648707c32e36bf", "instance_id": "django__django-12915", "instruction": "Add get_response_async for ASGIStaticFilesHandler\nDescription\n\t\nIt looks like the StaticFilesHandlerMixin is missing the the async response function.\nWithout this, when trying to use the ASGIStaticFilesHandler, this is the traceback:\nException inside application: 'NoneType' object is not callable\nTraceback (most recent call last):\n File \".../lib/python3.7/site-packages/daphne/cli.py\", line 30, in asgi\n\tawait self.app(scope, receive, send)\n File \".../src/django/django/contrib/staticfiles/handlers.py\", line 86, in __call__\n\treturn await super().__call__(scope, receive, send)\n File \".../src/django/django/core/handlers/asgi.py\", line 161, in __call__\n\tresponse = await self.get_response_async(request)\n File \".../src/django/django/core/handlers/base.py\", line 148, in get_response_async\n\tresponse = await self._middleware_chain(request)\nTypeError: 'NoneType' object is not callable", "metadata": {"created_at": "2020-05-14T23:30:01Z", "dataset": "swebench_lite", "fail_to_pass_count": 3, "pass_to_pass_count": 8, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-12915", "timeout_s": 1800, "verify": ["python -m pytest -q staticfiles_tests/test_handlers.py::TestASGIStaticFilesHandler::test_get_async_response", "python -m pytest -q staticfiles_tests/test_handlers.py::TestASGIStaticFilesHandler::test_get_async_response_not_found", "python -m pytest -q asgi/tests.py::ASGITest::test_static_file_response"]} +{"base_commit": "0e987498b00167fdd4a08a41c852a97cb70ce8f2", "instance_id": "sympy__sympy-16106", "instruction": "mathml printer for IndexedBase required\nWriting an `Indexed` object to MathML fails with a `TypeError` exception: `TypeError: 'Indexed' object is not iterable`:\r\n\r\n```\r\nIn [340]: sympy.__version__\r\nOut[340]: '1.0.1.dev'\r\n\r\nIn [341]: from sympy.abc import (a, b)\r\n\r\nIn [342]: sympy.printing.mathml(sympy.IndexedBase(a)[b])\r\n---------------------------------------------------------------------------\r\nTypeError Traceback (most recent call last)\r\n in ()\r\n----> 1 sympy.printing.mathml(sympy.IndexedBase(a)[b])\r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/mathml.py in mathml(expr, **settings)\r\n 442 def mathml(expr, **settings):\r\n 443 \"\"\"Returns the MathML representation of expr\"\"\"\r\n--> 444 return MathMLPrinter(settings).doprint(expr)\r\n 445 \r\n 446 \r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/mathml.py in doprint(self, expr)\r\n 36 Prints the expression as MathML.\r\n 37 \"\"\"\r\n---> 38 mathML = Printer._print(self, expr)\r\n 39 unistr = mathML.toxml()\r\n 40 xmlbstr = unistr.encode('ascii', 'xmlcharrefreplace')\r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/printer.py in _print(self, expr, *args, **kwargs)\r\n 255 printmethod = '_print_' + cls.__name__\r\n 256 if hasattr(self, printmethod):\r\n--> 257 return getattr(self, printmethod)(expr, *args, **kwargs)\r\n 258 # Unknown object, fall back to the emptyPrinter.\r\n 259 return self.emptyPrinter(expr)\r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/mathml.py in _print_Basic(self, e)\r\n 356 def _print_Basic(self, e):\r\n 357 x = self.dom.createElement(self.mathml_tag(e))\r\n--> 358 for arg in e:\r\n 359 x.appendChild(self._print(arg))\r\n 360 return x\r\n\r\nTypeError: 'Indexed' object is not iterable\r\n```\r\n\r\nIt also fails for more complex expressions where at least one element is Indexed.", "metadata": {"created_at": "2019-02-28T17:21:46Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Now it returns\r\n```\r\n'ab'\r\n```\r\nfor content printer and \r\n```\r\n'indexedindexedbaseab'\r\n```\r\nfor presentation printer", "pass_to_pass_count": 55, "version": "1.4"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-16106", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_print_IndexedBase"]} +{"base_commit": "96a02f3934952d486589dddd3f00b40d5a5ab5f2", "instance_id": "scikit-learn__scikit-learn-11040", "instruction": "Missing parameter validation in Neighbors estimator for float n_neighbors\n```python\r\nfrom sklearn.neighbors import NearestNeighbors\r\nfrom sklearn.datasets import make_blobs\r\nX, y = make_blobs()\r\nneighbors = NearestNeighbors(n_neighbors=3.)\r\nneighbors.fit(X)\r\nneighbors.kneighbors(X)\r\n```\r\n```\r\n~/checkout/scikit-learn/sklearn/neighbors/binary_tree.pxi in sklearn.neighbors.kd_tree.NeighborsHeap.__init__()\r\n\r\nTypeError: 'float' object cannot be interpreted as an integer\r\n```\r\nThis should be caught earlier and a more helpful error message should be raised (or we could be lenient and cast to integer, but I think a better error might be better).\r\n\r\nWe need to make sure that \r\n```python\r\nneighbors.kneighbors(X, n_neighbors=3.)\r\n```\r\nalso works.", "metadata": {"created_at": "2018-04-28T07:18:33Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Hello, I would like to take this as my first issue. \r\nThank you.\n@amueller \r\nI added a simple check for float inputs for n_neighbors in order to throw ValueError if that's the case.\n@urvang96 Did say he was working on it first @Alfo5123 ..\r\n\r\n@amueller I think there is a lot of", "pass_to_pass_count": 44, "version": "0.20"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-11040", "timeout_s": 1800, "verify": ["python -m pytest -q sklearn/neighbors/tests/test_neighbors.py::test_n_neighbors_datatype"]} +{"base_commit": "60dc957a825232fdda9138e2f8878b2ca407a7c9", "instance_id": "django__django-11583", "instruction": "Auto-reloading with StatReloader very intermittently throws \"ValueError: embedded null byte\".\nDescription\n\t\nRaising this mainly so that it's tracked, as I have no idea how to reproduce it, nor why it's happening. It ultimately looks like a problem with Pathlib, which wasn't used prior to 2.2.\nStacktrace:\nTraceback (most recent call last):\n File \"manage.py\" ...\n\texecute_from_command_line(sys.argv)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/__init__.py\", line 381, in execute_from_command_line\n\tutility.execute()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/__init__.py\", line 375, in execute\n\tself.fetch_command(subcommand).run_from_argv(self.argv)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/base.py\", line 323, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 60, in execute\n\tsuper().execute(*args, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/base.py\", line 364, in execute\n\toutput = self.handle(*args, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 95, in handle\n\tself.run(**options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 102, in run\n\tautoreload.run_with_reloader(self.inner_run, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 577, in run_with_reloader\n\tstart_django(reloader, main_func, *args, **kwargs)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 562, in start_django\n\treloader.run(django_main_thread)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 280, in run\n\tself.run_loop()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 286, in run_loop\n\tnext(ticker)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 326, in tick\n\tfor filepath, mtime in self.snapshot_files():\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 342, in snapshot_files\n\tfor file in self.watched_files():\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 241, in watched_files\n\tyield from iter_all_python_module_files()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 103, in iter_all_python_module_files\n\treturn iter_modules_and_files(modules, frozenset(_error_files))\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 132, in iter_modules_and_files\n\tresults.add(path.resolve().absolute())\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 1120, in resolve\n\ts = self._flavour.resolve(self, strict=strict)\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 346, in resolve\n\treturn _resolve(base, str(path)) or sep\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 330, in _resolve\n\ttarget = accessor.readlink(newpath)\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 441, in readlink\n\treturn os.readlink(path)\nValueError: embedded null byte\nI did print(path) before os.readlink(path) in pathlib and ended up with:\n/Users/kez\n/Users/kez/.pyenv\n/Users/kez/.pyenv/versions\n/Users/kez/.pyenv/versions/3.6.2\n/Users/kez/.pyenv/versions/3.6.2/lib\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/asyncio\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/asyncio/selector_events.py\n/Users\nIt always seems to be /Users which is last\nIt may have already printed /Users as part of another .resolve() multiple times (that is, the order is not deterministic, and it may have traversed beyond /Users successfully many times during startup.\nI don't know where to begin looking for the rogue null byte, nor why it only exists sometimes.\nBest guess I have is that there's a mountpoint in /Users to a samba share which may not have been connected to yet? I dunno.\nI have no idea if it's fixable without removing the use of pathlib (which tbh I think should happen anyway, because it's slow) and reverting to using os.path.join and friends. \nI have no idea if it's fixed in a later Python version, but with no easy way to reproduce ... dunno how I'd check.\nI have no idea if it's something specific to my system (pyenv, OSX 10.11, etc)", "metadata": {"created_at": "2019-07-21T20:56:14Z", "dataset": "swebench_lite", "fail_to_pass_count": 2, "hints_preview": "Thanks for the report, however as you've admitted there is too many unknowns to accept this ticket. I don't believe that it is related with pathlib, maybe samba connection is unstable it's hard to tell.\nI don't believe that it is related with pathlib Well ... it definitely is, yo", "pass_to_pass_count": 50, "version": "3.0"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-11583", "timeout_s": 1800, "verify": ["python -m pytest -q utils_tests/test_autoreload.py::TestIterModulesAndFiles::test_path_with_embedded_null_bytes", "python -m pytest -q utils_tests/test_autoreload.py::TestIterModulesAndFiles::test_paths_are_pathlib_instances"]} +{"base_commit": "4a72da71001f154ea60906a2f74898d32b7322a7", "instance_id": "django__django-17087", "instruction": "Class methods from nested classes cannot be used as Field.default.\nDescription\n\t \n\t\t(last modified by Mariusz Felisiak)\n\t \nGiven the following model:\n \nclass Profile(models.Model):\n\tclass Capability(models.TextChoices):\n\t\tBASIC = (\"BASIC\", \"Basic\")\n\t\tPROFESSIONAL = (\"PROFESSIONAL\", \"Professional\")\n\t\t\n\t\t@classmethod\n\t\tdef default(cls) -> list[str]:\n\t\t\treturn [cls.BASIC]\n\tcapabilities = ArrayField(\n\t\tmodels.CharField(choices=Capability.choices, max_length=30, blank=True),\n\t\tnull=True,\n\t\tdefault=Capability.default\n\t)\nThe resulting migration contained the following:\n # ...\n\t migrations.AddField(\n\t\t model_name='profile',\n\t\t name='capabilities',\n\t\t field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, choices=[('BASIC', 'Basic'), ('PROFESSIONAL', 'Professional')], max_length=30), default=appname.models.Capability.default, null=True, size=None),\n\t ),\n # ...\nAs you can see, migrations.AddField is passed as argument \"default\" a wrong value \"appname.models.Capability.default\", which leads to an error when trying to migrate. The right value should be \"appname.models.Profile.Capability.default\".", "metadata": {"created_at": "2023-07-17T20:28:41Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Thanks for the report. It seems that FunctionTypeSerializer should use __qualname__ instead of __name__: django/db/migrations/serializer.py diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index d88cda6e20..06657ebaab 100644 a b class FunctionT", "pass_to_pass_count": 53, "version": "5.0"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-17087", "timeout_s": 1800, "verify": ["python -m pytest -q migrations/test_writer/WriterTests.py::test_serialize_nested_class_method::test_serialize_nested_class_method"]} +{"base_commit": "1c8668b0a021832386470ddf740d834e02c66f69", "instance_id": "scikit-learn__scikit-learn-13142", "instruction": "GaussianMixture predict and fit_predict disagree when n_init>1\n#### Description\r\nWhen `n_init` is specified in GaussianMixture, the results of fit_predict(X) and predict(X) are often different. The `test_gaussian_mixture_fit_predict` unit test doesn't catch this because it does not set `n_init`.\r\n\r\n#### Steps/Code to Reproduce\r\n```\r\npython\r\nfrom sklearn.mixture import GaussianMixture\r\nfrom sklearn.utils.testing import assert_array_equal\r\nimport numpy\r\nX = numpy.random.randn(1000,5)\r\nprint 'no n_init'\r\ngm = GaussianMixture(n_components=5)\r\nc1 = gm.fit_predict(X)\r\nc2 = gm.predict(X)\r\nassert_array_equal(c1,c2)\r\nprint 'n_init=5'\r\ngm = GaussianMixture(n_components=5, n_init=5)\r\nc1 = gm.fit_predict(X)\r\nc2 = gm.predict(X)\r\nassert_array_equal(c1,c2)\r\n```\r\n\r\n#### Expected Results\r\n```\r\nno n_init\r\nn_init=5\r\n```\r\nNo exceptions.\r\n\r\n#### Actual Results\r\n```\r\nno n_init\r\nn_init=5\r\nTraceback (most recent call last):\r\n File \"test_gm.py\", line 17, in \r\n assert_array_equal(c1,c2)\r\n File \"/home/scott/.local/lib/python2.7/site-packages/numpy/testing/_private/utils.py\", line 872, in assert_array_equal\r\n verbose=verbose, header='Arrays are not equal')\r\n File \"/home/scott/.local/lib/python2.7/site-packages/numpy/testing/_private/utils.py\", line 796, in assert_array_compare\r\n raise AssertionError(msg)\r\nAssertionError: \r\nArrays are not equal\r\n\r\n(mismatch 88.6%)\r\n x: array([4, 0, 1, 1, 1, 3, 3, 4, 4, 2, 0, 0, 1, 2, 0, 2, 0, 1, 3, 1, 1, 3,\r\n 2, 1, 0, 2, 1, 0, 2, 0, 3, 1, 2, 3, 3, 1, 0, 2, 2, 0, 3, 0, 2, 0,\r\n 4, 2, 3, 0, 4, 2, 4, 1, 0, 2, 2, 1, 3, 2, 1, 4, 0, 2, 2, 1, 1, 2,...\r\n y: array([4, 1, 0, 2, 2, 1, 1, 4, 4, 0, 4, 1, 0, 3, 1, 0, 2, 2, 1, 2, 0, 0,\r\n 1, 0, 4, 1, 0, 4, 0, 1, 1, 2, 3, 1, 4, 0, 1, 4, 4, 4, 0, 1, 0, 2,\r\n 4, 1, 1, 2, 4, 3, 4, 0, 2, 3, 2, 3, 0, 0, 2, 3, 3, 3, 3, 0, 3, 2,...\r\n```\r\n\r\n#### Versions\r\n```\r\nSystem:\r\n python: 2.7.15rc1 (default, Nov 12 2018, 14:31:15) [GCC 7.3.0]\r\n machine: Linux-4.15.0-43-generic-x86_64-with-Ubuntu-18.04-bionic\r\nexecutable: /usr/bin/python\r\n\r\nBLAS:\r\n macros: HAVE_CBLAS=None, NO_ATLAS_INFO=-1\r\ncblas_libs: cblas\r\n lib_dirs: /usr/lib/x86_64-linux-gnu\r\n\r\nPython deps:\r\n Cython: 0.28.5\r\n scipy: 1.2.0\r\nsetuptools: 39.0.1\r\n pip: 19.0.1\r\n numpy: 1.16.0\r\n pandas: 0.23.1\r\n sklearn: 0.20.2\r\n```", "metadata": {"created_at": "2019-02-12T14:32:37Z", "dataset": "swebench_lite", "fail_to_pass_count": 2, "hints_preview": "Indeed the code in fit_predict and the one in predict are not exactly consistent. This should be fixed but we would need to check the math to choose the correct variant, add a test and remove the other one.\nI don't think the math is wrong or inconsistent. I think it's a matter o", "pass_to_pass_count": 54, "version": "0.21"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-13142", "timeout_s": 1800, "verify": ["python -m pytest -q sklearn/mixture/tests/test_bayesian_mixture.py::test_bayesian_mixture_fit_predict_n_init", "python -m pytest -q sklearn/mixture/tests/test_gaussian_mixture.py::test_gaussian_mixture_fit_predict_n_init"]} +{"base_commit": "29345aecf6e8d53ccb3577a3762bb0c263f7558d", "instance_id": "django__django-14382", "instruction": "django-admin startapp with trailing slash in directory name results in error\nDescription\n\t\nBash tab-completion appends trailing slashes to directory names. django-admin startapp name directory/ results in the error:\nCommandError: '' is not a valid app directory. Please make sure the directory is a valid identifier.\nThe error is caused by \u200bline 77 of django/core/management/templates.py by calling basename() on the path with no consideration for a trailing slash:\nself.validate_name(os.path.basename(target), 'directory')\nRemoving potential trailing slashes would solve the problem:\nself.validate_name(os.path.basename(target.rstrip(os.sep)), 'directory')", "metadata": {"created_at": "2021-05-11T10:40:42Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "OK, yes, this seems a case we could handle. I didn't look into exactly why but it works for startproject: $ django-admin startproject ticket32734 testing/ Thanks for the report. Do you fancy making a PR?\nI didn't look into exactly why but it works for startproject This is the rel", "pass_to_pass_count": 188, "version": "4.0"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-14382", "timeout_s": 1800, "verify": ["python -m pytest -q admin_scripts/tests.py::StartApp::test_trailing_slash_in_target_app_directory_name"]} +{"base_commit": "35b03788b0607c1f8d2b64e4fa9e1669b0907ea4", "instance_id": "django__django-13321", "instruction": "Decoding an invalid session data crashes.\nDescription\n\t \n\t\t(last modified by Matt Hegarty)\n\t \nHi\nI recently upgraded my staging server to 3.1. I think that there was an old session which was still active.\nOn browsing to any URL, I get the crash below. It looks similar to \u200bthis issue.\nI cannot login at all with Chrome - each attempt to access the site results in a crash. Login with Firefox works fine.\nThis is only happening on my Staging site, which is running Gunicorn behind nginx proxy.\nInternal Server Error: /overview/\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 215, in _get_session\nreturn self._session_cache\nAttributeError: 'SessionStore' object has no attribute '_session_cache'\nDuring handling of the above exception, another exception occurred:\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 118, in decode\nreturn signing.loads(session_data, salt=self.key_salt, serializer=self.serializer)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 135, in loads\nbase64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age).encode()\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 201, in unsign\nresult = super().unsign(value)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 184, in unsign\nraise BadSignature('Signature \"%s\" does not match' % sig)\ndjango.core.signing.BadSignature: Signature \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\" does not match\nDuring handling of the above exception, another exception occurred:\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py\", line 47, in inner\nresponse = get_response(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py\", line 179, in _get_response\nresponse = wrapped_callback(request, *callback_args, **callback_kwargs)\nFile \"/usr/local/lib/python3.8/site-packages/django/views/generic/base.py\", line 73, in view\nreturn self.dispatch(request, *args, **kwargs)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/mixins.py\", line 50, in dispatch\nif not request.user.is_authenticated:\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 240, in inner\nself._setup()\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 376, in _setup\nself._wrapped = self._setupfunc()\nFile \"/usr/local/lib/python3.8/site-packages/django_otp/middleware.py\", line 38, in _verify_user\nuser.otp_device = None\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 270, in __setattr__\nself._setup()\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 376, in _setup\nself._wrapped = self._setupfunc()\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/middleware.py\", line 23, in \nrequest.user = SimpleLazyObject(lambda: get_user(request))\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/middleware.py\", line 11, in get_user\nrequest._cached_user = auth.get_user(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/__init__.py\", line 174, in get_user\nuser_id = _get_user_session_key(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/__init__.py\", line 58, in _get_user_session_key\nreturn get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 65, in __getitem__\nreturn self._session[key]\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 220, in _get_session\nself._session_cache = self.load()\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/db.py\", line 44, in load\nreturn self.decode(s.session_data) if s else {}\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 122, in decode\nreturn self._legacy_decode(session_data)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 126, in _legacy_decode\nencoded_data = base64.b64decode(session_data.encode('ascii'))\nFile \"/usr/local/lib/python3.8/base64.py\", line 87, in b64decode\nreturn binascii.a2b_base64(s)\nbinascii.Error: Incorrect padding", "metadata": {"created_at": "2020-08-18T10:43:52Z", "dataset": "swebench_lite", "fail_to_pass_count": 355, "hints_preview": "I tried to run clearsessions, but that didn't help. The only workaround was to delete all rows in the django_session table.\nThanks for this report, however I cannot reproduce this issue. Can you provide a sample project? Support for user sessions created by older versions of Djan", "pass_to_pass_count": 0, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-13321", "timeout_s": 1800, "verify": ["python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_save_doesnt_clear_data", "python -m pytest -q -k 'Falsey values (Such as an empty string) are rejected.'", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_session_key_is_read_only", "python -m pytest -q -k 'Strings shorter than 8 characters are rejected.'", "python -m pytest -q -k 'Strings of length 8 and up are accepted and stored.'", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_unpickling_exception", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_create_and_save", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_load_overlong_key", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_non_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_empty_session_saved", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_flush_empty_without_session_cookie_doesnt_set_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_httponly_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_no_httponly_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_samesite_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_secure_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_delete_on_end", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_delete_on_end_with_custom_domain_and_path", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_save_on_500", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_update_error_redirect", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_clear", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_configuration_check", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_delete", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_flush", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_invalid_key_backslash", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_invalid_key_forwardslash", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_items", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_keys", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_save", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_store", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_update", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_values", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_configuration_check", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_invalid_key_backslash", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_invalid_key_forwardslash", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_get_decoded", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q -k 'Session repr should be the session key.'", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_sessionmanager_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_extra_session_field", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_get_decoded", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_sessionmanager_save", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_exists_searches_cache_first", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_load_overlong_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_non_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_exists_searches_cache_first", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_items", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_load_overlong_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_non_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_save", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_store", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_update", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_values", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_clear", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_delete", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_flush", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_items", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_keys", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_get_decoded", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_sessionmanager_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_store", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_update", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_values"]} +{"base_commit": "3bc4240d979812bd11365ede04c028ea13fdc8c6", "instance_id": "django__django-12983", "instruction": "Make django.utils.text.slugify() strip dashes and underscores\nDescription\n\t \n\t\t(last modified by Elinaldo do Nascimento Monteiro)\n\t \nBug generation slug\nExample:\nfrom django.utils import text\ntext.slugify(\"___This is a test ---\")\noutput: ___this-is-a-test-\nImprovement after correction\nfrom django.utils import text\ntext.slugify(\"___This is a test ---\")\noutput: this-is-a-test\n\u200bPR", "metadata": {"created_at": "2020-05-26T22:02:40Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "The current version of the patch converts all underscores to dashes which (as discussed on the PR) isn't an obviously desired change. A discussion is needed to see if there's consensus about that change.", "pass_to_pass_count": 15, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-12983", "timeout_s": 1800, "verify": ["python -m pytest -q utils_tests/test_text.py::TestUtilsText::test_slugify"]} +{"base_commit": "3ea1ec84cc610f7a9f4f6b354e264565254923ff", "instance_id": "sphinx-doc__sphinx-8474", "instruction": "v3.3 upgrade started generating \"WARNING: no number is assigned for table\" warnings\nWe've updated to Sphinx 3.3 in our documentation, and suddenly the following warning started popping up in our builds when we build either `singlehtml` or `latex`.:\r\n\r\n`WARNING: no number is assigned for table:`\r\n\r\nI looked through the changelog but it didn't seem like there was anything related to `numref` that was changed, but perhaps I missed something? Could anyone point me to a change in the numref logic so I can figure out where these warnings are coming from?", "metadata": {"created_at": "2020-11-22T16:24:25Z", "dataset": "swebench_lite", "fail_to_pass_count": 4, "hints_preview": "I digged into this a little bit more and it seems like the `id` of the table isn't properly making it into `env.toc_fignumbers`. If I set `:name: mylabel`, regardless the I see something like this in `env.toc_fignumbers`\r\n\r\n```\r\n 'pagename': {'table': {'id3': (1,)},\r\n```\r\n\r\nSo it", "pass_to_pass_count": 436, "version": "3.4"}, "repo": "sphinx-doc/sphinx", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sphinx-doc__sphinx-8474", "timeout_s": 1800, "verify": ["python -m pytest -q tests/test_build_html.py::test_numfig_without_numbered_toctree_warn", "python -m pytest -q tests/test_build_html.py::test_numfig_with_numbered_toctree_warn", "python -m pytest -q tests/test_build_html.py::test_numfig_with_prefix_warn", "python -m pytest -q tests/test_build_html.py::test_numfig_with_secnum_depth_warn"]} +{"base_commit": "e8c22f6eac7314be8d92590bfff92ced79ee03e2", "instance_id": "sympy__sympy-24213", "instruction": "collect_factor_and_dimension does not detect equivalent dimensions in addition\nCode to reproduce:\r\n```python\r\nfrom sympy.physics import units\r\nfrom sympy.physics.units.systems.si import SI\r\n\r\nv1 = units.Quantity('v1')\r\nSI.set_quantity_dimension(v1, units.velocity)\r\nSI.set_quantity_scale_factor(v1, 2 * units.meter / units.second)\r\n\r\na1 = units.Quantity('a1')\r\nSI.set_quantity_dimension(a1, units.acceleration)\r\nSI.set_quantity_scale_factor(a1, -9.8 * units.meter / units.second**2)\r\n\r\nt1 = units.Quantity('t1')\r\nSI.set_quantity_dimension(t1, units.time)\r\nSI.set_quantity_scale_factor(t1, 5 * units.second)\r\n\r\nexpr1 = a1*t1 + v1\r\nSI._collect_factor_and_dimension(expr1)\r\n```\r\nResults in:\r\n```\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"C:\\Python\\Python310\\lib\\site-packages\\sympy\\physics\\units\\unitsystem.py\", line 179, in _collect_factor_and_dimension\r\n raise ValueError(\r\nValueError: Dimension of \"v1\" is Dimension(velocity), but it should be Dimension(acceleration*time)\r\n```", "metadata": {"created_at": "2022-11-03T14:00:09Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 31, "version": "1.12"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-24213", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_issue_24211"]} +{"base_commit": "36bc47069ce071e80c8129500de3b8664d2058a7", "instance_id": "django__django-13315", "instruction": "limit_choices_to on a ForeignKey can render duplicate options in formfield\nDescription\n\t\nIf you pass a Q object as limit_choices_to on a ForeignKey field involving a join, you may end up with duplicate options in your form.\nSee regressiontest in patch for a clear view on the problem.", "metadata": {"created_at": "2020-08-17T04:24:39Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Replying to SmileyChris: I've updated the patch to resolve the conflicts I've had since you flagged this one as \"Ready for checkin\". No real change.\nupdate resolving conflict\nIs there something I can do to get this checked in? I re-read the \u200bTriage docs. As far as I can see \"A de", "pass_to_pass_count": 146, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-13315", "timeout_s": 1800, "verify": ["python -m pytest -q model_forms/tests.py::LimitChoicesToTests::test_limit_choices_to_no_duplicates"]} +{"base_commit": "02dc9ed680e7f53f1b0d410dcdd37341c7958eb1", "instance_id": "scikit-learn__scikit-learn-12471", "instruction": "OneHotEncoder ignore unknown error when categories are strings \n#### Description\r\n\r\nThis bug is very specific, but it happens when you set OneHotEncoder to ignore unknown entries.\r\nand your labels are strings. The memory of the arrays is not handled safely and it can lead to a ValueError\r\n\r\nBasically, when you call the transform method it will sets all the unknown strings on your array to OneHotEncoder.categories_[i][0] which is the first category alphabetically sorted given for fit\r\nIf this OneHotEncoder.categories_[i][0] is a long string, and the array that you want to transform has small strings, then it is impossible to fit the whole OneHotEncoder.categories_[i][0] into the entries of the array we want to transform. So OneHotEncoder.categories_[i][0] is truncated and this raise the ValueError.\r\n\r\n\r\n\r\n#### Steps/Code to Reproduce\r\n```\r\n\r\nimport numpy as np\r\nfrom sklearn.preprocessing import OneHotEncoder\r\n\r\n\r\n# It needs to be numpy arrays, the error does not appear \r\n# is you have lists of lists because it gets treated like an array of objects.\r\ntrain = np.array([ '22','333','4444','11111111' ]).reshape((-1,1))\r\ntest = np.array([ '55555', '22' ]).reshape((-1,1))\r\n\r\nohe = OneHotEncoder(dtype=bool,handle_unknown='ignore')\r\n\r\nohe.fit( train )\r\nenc_test = ohe.transform( test )\r\n\r\n```\r\n\r\n\r\n#### Expected Results\r\nHere we should get an sparse matrix 2x4 false everywhere except at (1,1) the '22' that is known\r\n\r\n#### Actual Results\r\n\r\n> ValueError: y contains previously unseen labels: ['111111']\r\n\r\n\r\n#### Versions\r\nSystem:\r\n python: 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609]\r\n machine: Linux-4.4.0-138-generic-x86_64-with-Ubuntu-16.04-xenial\r\nexecutable: /usr/bin/python\r\n\r\nBLAS:\r\n macros: HAVE_CBLAS=None\r\ncblas_libs: openblas, openblas\r\n lib_dirs: /usr/lib\r\n\r\nPython deps:\r\n Cython: 0.25.2\r\n scipy: 0.18.1\r\nsetuptools: 36.7.0\r\n pip: 9.0.1\r\n numpy: 1.15.2\r\n pandas: 0.19.1\r\n sklearn: 0.21.dev0\r\n\r\n\r\n\r\n#### Comments\r\n\r\nI already implemented a fix for this issue, where I check the size of the elements in the array before, and I cast them into objects if necessary.", "metadata": {"created_at": "2018-10-27T10:43:48Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 53, "version": "0.21"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-12471", "timeout_s": 1800, "verify": ["python -m pytest -q sklearn/preprocessing/tests/test_encoders.py::test_one_hot_encoder_handle_unknown_strings"]} +{"base_commit": "56bf819c2f4eaf8b36bd8c42c06bb59d5a3bfc0f", "instance_id": "pytest-dev__pytest-7220", "instruction": "Wrong path to test file when directory changed in fixture\nFiles are shown as relative to new directory when working directory is changed in a fixture. This makes it impossible to jump to the error as the editor is unaware of the directory change. The displayed directory should stay relative to the original directory.\r\n\r\ntest_path_error.py:\r\n```python\r\nimport os\r\nimport errno\r\nimport shutil\r\n\r\nimport pytest\r\n\r\n\r\n@pytest.fixture\r\ndef private_dir(): # or (monkeypatch)\r\n out_dir = 'ddd'\r\n\r\n try:\r\n shutil.rmtree(out_dir)\r\n except OSError as ex:\r\n if ex.errno != errno.ENOENT:\r\n raise\r\n os.mkdir(out_dir)\r\n\r\n old_dir = os.getcwd()\r\n os.chdir(out_dir)\r\n yield out_dir\r\n os.chdir(old_dir)\r\n\r\n # Same issue if using:\r\n # monkeypatch.chdir(out_dir)\r\n\r\n\r\ndef test_show_wrong_path(private_dir):\r\n assert False\r\n```\r\n\r\n```diff\r\n+ Expected: test_path_error.py:29: AssertionError\r\n- Displayed: ../test_path_error.py:29: AssertionError\r\n```\r\n\r\nThe full output is:\r\n```\r\n-*- mode: compilation; default-directory: \"~/src/pytest_path_error/\" -*-\r\nCompilation started at Fri Jan 10 00:05:52\r\n\r\nnox\r\nnox > Running session test\r\nnox > Creating virtual environment (virtualenv) using python3.7 in .nox/test\r\nnox > pip install pytest>=5.3\r\nnox > pip freeze\r\nattrs==19.3.0\r\nimportlib-metadata==1.3.0\r\nmore-itertools==8.0.2\r\npackaging==20.0\r\npluggy==0.13.1\r\npy==1.8.1\r\npyparsing==2.4.6\r\npytest==5.3.2\r\nsix==1.13.0\r\nwcwidth==0.1.8\r\nzipp==0.6.0\r\nnox > pytest \r\n================================= test session starts =================================\r\nplatform linux -- Python 3.7.5, pytest-5.3.2, py-1.8.1, pluggy-0.13.1\r\nrootdir: /home/lhn/src/pytest_path_error\r\ncollected 1 item \r\n\r\ntest_path_error.py F [100%]\r\n\r\n====================================== FAILURES =======================================\r\n________________________________ test_show_wrong_path _________________________________\r\n\r\nprivate_dir = 'ddd'\r\n\r\n def test_show_wrong_path(private_dir):\r\n> assert False\r\nE assert False\r\n\r\n../test_path_error.py:29: AssertionError\r\n================================== 1 failed in 0.03s ==================================\r\nnox > Command pytest failed with exit code 1\r\nnox > Session test failed.\r\n\r\nCompilation exited abnormally with code 1 at Fri Jan 10 00:06:01\r\n```\r\n\r\nnoxfile.py:\r\n```python\r\nimport nox\r\n\r\n@nox.session(python='3.7')\r\ndef test(session):\r\n session.install('pytest>=5.3')\r\n session.run('pip', 'freeze')\r\n session.run('pytest')\r\n```", "metadata": {"created_at": "2020-05-16T14:57:17Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 11, "version": "5.4"}, "repo": "pytest-dev/pytest", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::pytest-dev__pytest-7220", "timeout_s": 1800, "verify": ["python -m pytest -q testing/test_nodes.py::test_failure_with_changed_cwd"]} diff --git a/scripts/swe/build_mini_subset.py b/scripts/swe/build_mini_subset.py new file mode 100644 index 000000000..d4c40a81b --- /dev/null +++ b/scripts/swe/build_mini_subset.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +"""Build a deterministic 20-task SWE mini subset. + +Examples +-------- +Build from the Hugging Face datasets-server API (default): + + uv run python scripts/swe/build_mini_subset.py + +Build from a local JSONL dump: + + uv run python scripts/swe/build_mini_subset.py \ + --input-jsonl /path/to/swebench_lite_rows.jsonl +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import sys +from typing import Any +from urllib.error import HTTPError, URLError +from urllib.parse import urlencode +from urllib.request import urlopen + +REPO_ROOT = Path(__file__).resolve().parents[2] +ENVS_DIR = REPO_ROOT / "envs" +if str(ENVS_DIR) not in sys.path: + sys.path.insert(0, str(ENVS_DIR)) + +from mini_swe_env.task_loader_swebench_lite import ( # noqa: E402 + adapt_swebench_lite_rows, + deterministic_train_eval_split, + load_task_file, + read_jsonl_rows, + write_tasks_jsonl, +) + +DEFAULT_DATASET = "princeton-nlp/SWE-bench_Lite" +DEFAULT_SPLIT = "test" +DEFAULT_TRAIN_OUTPUT = REPO_ROOT / "examples/mini_swe_env/tasks/mini_swe_train.jsonl" +DEFAULT_EVAL_OUTPUT = REPO_ROOT / "examples/mini_swe_env/tasks/mini_swe_eval.jsonl" +DATASET_SERVER_ROWS_URL = "https://datasets-server.huggingface.co/rows" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--input-jsonl", + type=Path, + default=None, + help="Optional local SWE-bench Lite JSONL rows. If omitted, fetches from datasets-server.", + ) + parser.add_argument( + "--dataset", + default=DEFAULT_DATASET, + help=f"HF dataset id when fetching remotely (default: {DEFAULT_DATASET}).", + ) + parser.add_argument( + "--split", + default=DEFAULT_SPLIT, + help=f"Dataset split when fetching remotely (default: {DEFAULT_SPLIT}).", + ) + parser.add_argument( + "--max-rows", + type=int, + default=None, + help="Optional cap for remotely fetched rows (useful for smoke runs).", + ) + parser.add_argument("--seed", type=int, default=17) + parser.add_argument("--subset-size", type=int, default=20) + parser.add_argument("--train-size", type=int, default=16) + parser.add_argument("--strict", action="store_true") + parser.add_argument( + "--train-output", + type=Path, + default=DEFAULT_TRAIN_OUTPUT, + ) + parser.add_argument( + "--eval-output", + type=Path, + default=DEFAULT_EVAL_OUTPUT, + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + + if args.input_jsonl: + rows = read_jsonl_rows(args.input_jsonl) + source_desc = str(args.input_jsonl) + else: + rows = fetch_rows_from_dataset_server( + dataset=args.dataset, + split=args.split, + max_rows=args.max_rows, + ) + source_desc = f"hf://{args.dataset}[{args.split}]" + + tasks, skipped = adapt_swebench_lite_rows(rows, strict=args.strict) + train_tasks, eval_tasks = deterministic_train_eval_split( + tasks, + subset_size=args.subset_size, + train_size=args.train_size, + seed=args.seed, + ) + + write_tasks_jsonl(args.train_output, train_tasks) + write_tasks_jsonl(args.eval_output, eval_tasks) + + # Final safety check: output must parse as valid SWETask rows. + _ = load_task_file(args.train_output) + _ = load_task_file(args.eval_output) + + print( + "Built mini SWE subset:", + json.dumps( + { + "source": source_desc, + "seed": args.seed, + "rows_seen": len(rows), + "valid_tasks": len(tasks), + "skipped_rows": len(skipped), + "train_tasks": len(train_tasks), + "eval_tasks": len(eval_tasks), + "train_output": str(args.train_output), + "eval_output": str(args.eval_output), + }, + indent=2, + sort_keys=True, + ), + ) + + if skipped: + print("Sample skipped rows (up to 5):") + for skip in skipped[:5]: + print( + f" line={skip.row_index} instance_id={skip.instance_id or '-'} " + f"reason={skip.reason}" + ) + + +def fetch_rows_from_dataset_server( + *, dataset: str, split: str, max_rows: int | None +) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + offset = 0 + page_size = 100 + + while True: + if max_rows is not None: + remaining = max_rows - len(rows) + if remaining <= 0: + break + page_length = min(page_size, remaining) + else: + page_length = page_size + + payload = _fetch_rows_page( + dataset=dataset, + split=split, + offset=offset, + length=page_length, + ) + page_rows = payload.get("rows", []) + if not page_rows: + break + + for item in page_rows: + row = item.get("row") if isinstance(item, dict) else None + if isinstance(row, dict): + rows.append(row) + + offset += len(page_rows) + + total = payload.get("num_rows_total") + if isinstance(total, int) and offset >= total: + break + + if len(page_rows) < page_length: + break + + if not rows: + raise RuntimeError(f"No rows fetched for dataset={dataset!r} split={split!r}.") + return rows + + +def _fetch_rows_page( + *, dataset: str, split: str, offset: int, length: int +) -> dict[str, Any]: + query = urlencode( + { + "dataset": dataset, + "config": "default", + "split": split, + "offset": offset, + "length": length, + } + ) + url = f"{DATASET_SERVER_ROWS_URL}?{query}" + try: + with urlopen(url, timeout=60) as response: + data = json.loads(response.read().decode("utf-8")) + except HTTPError as exc: + raise RuntimeError( + f"datasets-server request failed ({exc.code}) for {url}: {exc.reason}" + ) from exc + except URLError as exc: + raise RuntimeError( + f"datasets-server request failed for {url}: {exc.reason}" + ) from exc + + if not isinstance(data, dict): + raise RuntimeError( + f"datasets-server response must be a JSON object, got {type(data).__name__}" + ) + return data + + +if __name__ == "__main__": + main() diff --git a/tests/envs/test_swe_task_loader.py b/tests/envs/test_swe_task_loader.py new file mode 100644 index 000000000..6ac474b46 --- /dev/null +++ b/tests/envs/test_swe_task_loader.py @@ -0,0 +1,136 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Unit tests for SWE-bench Lite -> SWETask adaptation.""" + +from __future__ import annotations + +import json +import random + +import pytest +from mini_swe_env.task_loader_swebench_lite import ( + adapt_swebench_lite_row, + coerce_swe_task, + deterministic_train_eval_split, + SOURCE_NAME, + SWEBenchLiteAdapterError, + SWETask, + SWETaskValidationError, +) + + +def test_row_to_swetask_conversion_uses_fail_to_pass_for_verify() -> None: + row = { + "repo": "psf/requests", + "instance_id": "requests__requests-12345", + "base_commit": "a" * 40, + "problem_statement": "Fix redirect edge case in Session.send.", + "FAIL_TO_PASS": json.dumps( + [ + "tests/test_sessions.py::test_redirect_loop", + "tests/test_sessions.py::test_strip_auth", + ] + ), + "PASS_TO_PASS": json.dumps(["tests/test_api.py::test_get"]), + "created_at": "2024-01-01", + "version": "1.0", + } + + task = adapt_swebench_lite_row(row) + + assert task.source == SOURCE_NAME + assert task.task_id == f"{SOURCE_NAME}::{row['instance_id']}" + assert task.instance_id == row["instance_id"] + assert task.repo == row["repo"] + assert task.base_commit == row["base_commit"] + assert task.setup == [] + assert task.verify == [ + "python -m pytest -q tests/test_sessions.py::test_redirect_loop", + "python -m pytest -q tests/test_sessions.py::test_strip_auth", + ] + assert task.metadata["dataset"] == SOURCE_NAME + assert task.metadata["fail_to_pass_count"] == 2 + assert task.metadata["pass_to_pass_count"] == 1 + + +def test_deterministic_split_is_stable_for_seed_and_input_order() -> None: + tasks = [ + SWETask( + task_id=f"{SOURCE_NAME}::id-{idx:03d}", + source=SOURCE_NAME, + instance_id=f"id-{idx:03d}", + repo="example/repo", + base_commit="b" * 40, + instruction=f"Task {idx}", + setup=[], + verify=["python -m pytest -q tests/test_sample.py::test_ok"], + ) + for idx in range(40) + ] + + train_a, eval_a = deterministic_train_eval_split( + tasks, + subset_size=20, + train_size=16, + seed=99, + ) + + shuffled = tasks[:] + random.Random(123).shuffle(shuffled) + train_b, eval_b = deterministic_train_eval_split( + shuffled, + subset_size=20, + train_size=16, + seed=99, + ) + + assert [task.task_id for task in train_a] == [task.task_id for task in train_b] + assert [task.task_id for task in eval_a] == [task.task_id for task in eval_b] + + +def test_row_conversion_handles_unittest_style_names() -> None: + row = { + "repo": "django/django", + "instance_id": "django__django-12915", + "base_commit": "d" * 40, + "problem_statement": "Add async handler method.", + "FAIL_TO_PASS": json.dumps( + [ + "test_get_async_response (staticfiles_tests.test_handlers.TestASGIStaticFilesHandler)" + ] + ), + } + + task = adapt_swebench_lite_row(row) + + assert task.verify == [ + "python -m pytest -q " + "staticfiles_tests/test_handlers.py::TestASGIStaticFilesHandler::test_get_async_response" + ] + + +def test_schema_validation_failures_are_explicit() -> None: + with pytest.raises( + SWETaskValidationError, match="verify must contain at least one" + ): + coerce_swe_task( + { + "task_id": "x", + "source": SOURCE_NAME, + "instance_id": "x", + "repo": "org/repo", + "base_commit": "c" * 40, + "instruction": "Do something", + "setup": [], + "verify": [], + } + ) + + +def test_adapter_error_is_explicit_when_required_fields_missing() -> None: + with pytest.raises(SWEBenchLiteAdapterError, match="missing required field"): + adapt_swebench_lite_row({"instance_id": "missing_repo"}) From 6c4caa4dc47e57f46872f27f57517b6d188ee24a Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 11:43:30 +0530 Subject: [PATCH 17/79] feat: mini_swe_env impl --- envs/mini_swe_env/README.md | 158 +++++ envs/mini_swe_env/__init__.py | 24 +- envs/mini_swe_env/client.py | 166 +++++ envs/mini_swe_env/models.py | 73 ++ envs/mini_swe_env/server/__init__.py | 7 + envs/mini_swe_env/server/app.py | 52 ++ .../mini_swe_env/server/sandbox_mcp_server.py | 425 ++++++++++++ envs/mini_swe_env/server/swe_environment.py | 656 ++++++++++++++++++ 8 files changed, 1560 insertions(+), 1 deletion(-) create mode 100644 envs/mini_swe_env/README.md create mode 100644 envs/mini_swe_env/client.py create mode 100644 envs/mini_swe_env/models.py create mode 100644 envs/mini_swe_env/server/__init__.py create mode 100644 envs/mini_swe_env/server/app.py create mode 100644 envs/mini_swe_env/server/sandbox_mcp_server.py create mode 100644 envs/mini_swe_env/server/swe_environment.py diff --git a/envs/mini_swe_env/README.md b/envs/mini_swe_env/README.md new file mode 100644 index 000000000..8991c66f1 --- /dev/null +++ b/envs/mini_swe_env/README.md @@ -0,0 +1,158 @@ +# Mini SWE Environment + +A training environment for SWE-bench Lite tasks using OpenEnv. + +## Overview + +`mini_swe_env` provides an MCP-based environment that: + +1. **Provisions a sandbox** (Docker, E2B, or HF Sandbox) +2. **Clones a Git repository** at a specified base commit +3. **Runs setup commands** to prepare the environment +4. **Starts an in-sandbox MCP tool server** exposing a `terminal` tool +5. **Launches a coding agent** (Pi, OpenCode) that uses `terminal` to explore and fix code +6. **Runs verification commands** after the agent finishes +7. **Computes reward** (passed/total verify commands, or explicit override) + +## Architecture + +``` +┌─────────────────────────────────────────────────────┐ +│ Training Script / Client │ +│ env.run_swe_rollout(task=..., agent="pi", ...) │ +└─────────────┬───────────────────────────────────────┘ + │ MCP tool call + ▼ +┌─────────────────────────────────────────────────────┐ +│ SWEEnvironment (OpenEnv Server) │ +│ - Creates sandbox │ +│ - Clones repo at base_commit │ +│ - Runs setup commands │ +│ - Deploys in-sandbox MCP server │ +│ - Launches agent │ +│ - Runs verify → computes reward │ +└─────────────┬───────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ Sandbox (Docker / E2B / HF) │ +│ ┌─────────────────────────────────────────────┐ │ +│ │ sandbox_mcp_server.py (port 8765) │ │ +│ │ └─ terminal(command=...) → execute shell │ │ +│ │ └─ terminal(final_answer=...) → verify │ │ +│ └─────────────────┬───────────────────────────┘ │ +│ │ MCP tool calls │ +│ ┌─────────────────▼───────────────────────────┐ │ +│ │ Agent (Pi / OpenCode) │ │ +│ │ Explores code, makes changes, submits fix │ │ +│ └─────────────────────────────────────────────┘ │ +│ │ +│ /home/user/workdir/ ← cloned repo at base_commit │ +└─────────────────────────────────────────────────────┘ +``` + +## Quick Start + +### As a Client + +```python +from mini_swe_env import MiniSWEEnv + +with MiniSWEEnv(base_url="http://localhost:8000") as env: + env.reset() + result = env.run_swe_rollout( + instance_id="requests__requests-12345", + repo="psf/requests", + base_commit="abc123...", + instruction="Fix the redirect edge case in Session.send.", + verify=["python -m pytest tests/test_redirect.py -q"], + agent="pi", + base_url="https://api.openai.com/v1", + api_key="sk-...", + model="gpt-4o-mini", + ) + print(f"Reward: {result.reward}") + print(f"Files changed: {list(result.files.keys())}") +``` + +### From a Task File + +```python +from mini_swe_env import MiniSWEEnv, load_task_file +import json + +tasks = load_task_file("examples/mini_swe_env/tasks/mini_swe_train.jsonl") + +with MiniSWEEnv(base_url="http://localhost:8000") as env: + env.reset() + for task in tasks: + result = env.run_swe_rollout( + task_json=json.dumps(task.to_dict()), + agent="pi", + base_url="https://api.openai.com/v1", + api_key="sk-...", + model="gpt-4o-mini", + ) + print(f"{task.instance_id}: reward={result.reward}") +``` + +### Running the Server + +```bash +# Local development +PYTHONPATH=src:envs uvicorn mini_swe_env.server.app:app --host 0.0.0.0 --port 8000 + +# With Docker backend (default) +PYTHONPATH=src:envs python -m mini_swe_env.server.app +``` + +## Task Shape (SWETask) + +```python +@dataclass +class SWETask: + task_id: str # Unique identifier + source: str # "swebench_lite" + instance_id: str # SWE-bench instance id + repo: str # "org/repo" + base_commit: str # Git commit hash + instruction: str # Problem statement + setup: list[str] # Commands before agent starts + verify: list[str] # Commands after agent finishes + timeout_s: int = 1800 # Total timeout + sandbox_image: str | None # Optional custom image + metadata: dict # Additional info +``` + +## Terminal Tool + +The in-sandbox MCP server exposes a single `terminal` tool: + +### Execute Command +```json +{"command": "git diff HEAD"} +``` +Returns: `{"output": "...", "stderr": "...", "exit_code": 0, "done": false}` + +### Submit Final Answer +```json +{"final_answer": "I fixed the bug by changing line 42 in utils.py..."} +``` +Returns: `{"reward": 0.75, "verify_results": [...], "done": true}` + +## Reward Computation + +1. **Default**: `passed_verify_commands / total_verify_commands` +2. **Override**: Any verify command (or the agent) can write a float to + `/home/user/logs/verifier/reward.txt` to override the default. + +## Components + +| File | Purpose | +|------|---------| +| `task_loader_swebench_lite.py` | SWE-bench Lite → SWETask adapter | +| `models.py` | Pydantic models (SWERolloutResult, SWEState) | +| `server/swe_environment.py` | Main environment (MCPEnvironment) | +| `server/sandbox_mcp_server.py` | In-sandbox terminal tool server | +| `server/app.py` | FastAPI application | +| `client.py` | Typed MiniSWEEnv client | \ No newline at end of file diff --git a/envs/mini_swe_env/__init__.py b/envs/mini_swe_env/__init__.py index 7b604d3f7..43535d50f 100644 --- a/envs/mini_swe_env/__init__.py +++ b/envs/mini_swe_env/__init__.py @@ -1,4 +1,12 @@ -"""SWE environment package task adapter surface.""" +"""Mini SWE environment — SWE-bench task adapter + environment. + +Exports: + Task adapter (Phase 2): + SWETask, validate_swe_task, adapt_swebench_lite_row, ... + + Environment + client (Phase 3): + SWEEnvironment, MiniSWEEnv, SWERolloutResult, ... +""" from .task_loader_swebench_lite import ( adapt_swebench_lite_row, @@ -15,7 +23,16 @@ write_tasks_jsonl, ) +from .models import ( + SWECommandResult, + SWERolloutResult, + SWEState, +) + +from .client import MiniSWEEnv + __all__ = [ + # Task adapter (Phase 2) "AdaptationSkip", "SWEBenchLiteAdapterError", "SWETask", @@ -28,4 +45,9 @@ "read_jsonl_rows", "validate_swe_task", "write_tasks_jsonl", + # Models (Phase 3) + "MiniSWEEnv", + "SWECommandResult", + "SWERolloutResult", + "SWEState", ] diff --git a/envs/mini_swe_env/client.py b/envs/mini_swe_env/client.py new file mode 100644 index 000000000..fe5f359bb --- /dev/null +++ b/envs/mini_swe_env/client.py @@ -0,0 +1,166 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Client for the Mini SWE Environment. + +Provides a typed interface for running SWE-bench rollouts through the +deployed MCP server. + +Example:: + + from mini_swe_env import MiniSWEEnv + + with MiniSWEEnv(base_url="http://localhost:8000") as env: + env.reset() + result = env.run_swe_rollout( + instance_id="requests__requests-12345", + repo="psf/requests", + base_commit="abc123...", + instruction="Fix the redirect edge case...", + verify=["python -m pytest tests/test_redirect.py -q"], + base_url="https://api.openai.com/v1", + api_key=os.environ["OPENAI_API_KEY"], + model="gpt-4o-mini", + agent="pi", + ) + print(f"Reward: {result.reward}") +""" + +from __future__ import annotations + +import json +from typing import Any + +from openenv.core.mcp_client import MCPToolClient + +try: + from .models import SWERolloutResult +except ImportError: # pragma: no cover + from models import SWERolloutResult # type: ignore + + +class MiniSWEEnv(MCPToolClient): + """Typed client for the mini_swe_env MCP server. + + Inherits ``reset`` / ``call_tool`` / ``list_tools`` / ``from_docker_image`` + / context-manager semantics from :class:`MCPToolClient`. + """ + + def run_swe_rollout( + self, + *, + # Task fields (SWETask shape). + instance_id: str = "", + repo: str = "", + base_commit: str = "", + instruction: str = "", + setup: list[str] | None = None, + verify: list[str] | None = None, + timeout_s: int = 1800, + # Agent config. + agent: str = "pi", + base_url: str = "", + api_key: str = "", + model: str = "", + agent_timeout_s: float = 600.0, + # Infrastructure. + sandbox_backend: str = "docker", + sandbox_image: str = "", + task_id: str = "", + task_json: str = "", + ) -> SWERolloutResult: + """Run one SWE rollout and return the typed result. + + Args: + instance_id: SWE-bench Lite instance id. + repo: GitHub ``org/repo`` to clone. + base_commit: Commit to reset the repo to. + instruction: Problem statement for the agent. + setup: Bash commands run before the agent starts. + verify: Bash commands run after the agent exits. + Reward = ``passed / total`` unless a command writes a float + to ``/home/user/logs/verifier/reward.txt`` (override). + timeout_s: Total timeout for the task. + agent: Harness CLI (``"pi"`` or ``"opencode"``). + base_url: OpenAI-compatible LLM endpoint. + api_key: Bearer token for the LLM. + model: Model id for the LLM endpoint. + agent_timeout_s: Wall-clock budget for the agent run. + sandbox_backend: ``"docker"`` / ``"e2b"`` / ``"hf"``. + sandbox_image: Docker image or E2B template. Empty = default. + task_id: Echoed back in the result for traceability. + task_json: Complete SWETask as JSON string (overrides individual fields). + + Returns: + A :class:`SWERolloutResult` with reward, verify results, + file outputs, and diagnostic tails. + """ + raw = self.call_tool( + "run_swe_rollout", + instance_id=instance_id, + repo=repo, + base_commit=base_commit, + instruction=instruction, + setup=list(setup or []), + verify=list(verify or []), + timeout_s=timeout_s, + agent=agent, + base_url=base_url, + api_key=api_key, + model=model, + agent_timeout_s=agent_timeout_s, + sandbox_backend=sandbox_backend, + sandbox_image=sandbox_image, + task_id=task_id, + task_json=task_json, + ) + return SWERolloutResult.model_validate_json(_extract_text(raw)) + + +def _extract_text(result: Any) -> str: + """Pull the JSON text out of whatever shape the MCP layer returns. + + Handles the three shapes :meth:`MCPToolClient.call_tool` may surface: + a raw string, a ``CallToolObservation``-like object with + ``.result.content[0].text``, or a dict with ``content[0]["text"]``. + """ + if isinstance(result, str): + return result + + inner = getattr(result, "result", None) + if inner is not None: + content = getattr(inner, "content", None) + if content: + first = content[0] + text = getattr(first, "text", None) + if isinstance(text, str): + return text + if isinstance(first, dict) and "text" in first: + return first["text"] + + if isinstance(result, dict): + content = result.get("content") + if isinstance(content, list) and content: + first = content[0] + if isinstance(first, dict) and "text" in first: + return first["text"] + nested = result.get("result") + if isinstance(nested, dict): + content = nested.get("content") + if isinstance(content, list) and content: + first = content[0] + if isinstance(first, dict) and "text" in first: + return first["text"] + return json.dumps(result, default=str) + + content = getattr(result, "content", None) + if content: + first = content[0] + text = getattr(first, "text", None) + if isinstance(text, str): + return text + + return str(result) diff --git a/envs/mini_swe_env/models.py b/envs/mini_swe_env/models.py new file mode 100644 index 000000000..f79a7a25b --- /dev/null +++ b/envs/mini_swe_env/models.py @@ -0,0 +1,73 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Pydantic models for the mini_swe_env server. + +The server exposes a single MCP tool ``run_swe_rollout`` that takes an +SWE task, runs one agent rollout inside a sandbox, and returns a +JSON-serialized :class:`SWERolloutResult`. +""" + +from __future__ import annotations + +from openenv.core.env_server.types import State +from pydantic import BaseModel, Field + + +class SWECommandResult(BaseModel): + """Outcome of one shell command in setup or verify.""" + + cmd: str + exit_code: int + stdout: str = "" + stderr: str = "" + duration_s: float = 0.0 + + +class SWERolloutResult(BaseModel): + """Full payload returned from one ``run_swe_rollout`` invocation. + + The trainer (or any client) decodes this from the MCP tool result JSON + and feeds ``reward`` into GRPO. + """ + + # Identifiers + task_id: str = "" + instance_id: str = "" + sandbox_id: str = "" + + # Scalars + reward: float | None = None + agent_exit_code: int | None = None + wall_s: float = 0.0 + + # Per-step results + setup_results: list[SWECommandResult] = Field(default_factory=list) + verify_results: list[SWECommandResult] = Field(default_factory=list) + + # Filesystem the agent produced (path -> contents, truncated) + files: dict[str, str] = Field(default_factory=dict) + files_extra: list[str] = Field(default_factory=list) + + # Diagnostic tails + agent_log_tail: str = "" + + # Error surfacing + error: str | None = None + + +class SWEState(State): + """Per-session environment state across calls to one SWEEnvironment instance. + + Each HTTP session gets its own env (``SUPPORTS_CONCURRENT_SESSIONS=True`` + on the server class), so this state is per-session. + """ + + rollouts_completed: int = 0 + last_reward: float | None = None + last_task_id: str | None = None + last_instance_id: str | None = None + last_sandbox_id: str | None = None diff --git a/envs/mini_swe_env/server/__init__.py b/envs/mini_swe_env/server/__init__.py new file mode 100644 index 000000000..4422a48c2 --- /dev/null +++ b/envs/mini_swe_env/server/__init__.py @@ -0,0 +1,7 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Mini SWE environment server package.""" diff --git a/envs/mini_swe_env/server/app.py b/envs/mini_swe_env/server/app.py new file mode 100644 index 000000000..1039602ac --- /dev/null +++ b/envs/mini_swe_env/server/app.py @@ -0,0 +1,52 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""FastAPI application for the Mini SWE Environment. + +Exposes the SWEEnvironment over HTTP and WebSocket endpoints, +compatible with MCPToolClient. + +Usage: + # Development: + PYTHONPATH=src:envs uvicorn mini_swe_env.server.app:app --reload --port 8000 + + # Production: + uvicorn mini_swe_env.server.app:app --host 0.0.0.0 --port 8000 +""" + +import os + +try: + from openenv.core.env_server.http_server import create_app + from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation + + from .swe_environment import SWEEnvironment +except ImportError: # pragma: no cover + from openenv.core.env_server.http_server import create_app + from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation + from server.swe_environment import SWEEnvironment # type: ignore + + +max_concurrent = int(os.getenv("MAX_CONCURRENT_ENVS", "4")) + +app = create_app( + SWEEnvironment, + CallToolAction, + CallToolObservation, + env_name="mini_swe_env", + max_concurrent_envs=max_concurrent, +) + + +def main() -> None: + """Entry point for ``uv run --project . server`` and direct invocation.""" + import uvicorn + + uvicorn.run(app, host="0.0.0.0", port=8000) + + +if __name__ == "__main__": + main() diff --git a/envs/mini_swe_env/server/sandbox_mcp_server.py b/envs/mini_swe_env/server/sandbox_mcp_server.py new file mode 100644 index 000000000..2867b89bd --- /dev/null +++ b/envs/mini_swe_env/server/sandbox_mcp_server.py @@ -0,0 +1,425 @@ +#!/usr/bin/env python3 +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Lightweight MCP tool server deployed INSIDE the sandbox. + +This script is written into the sandbox by the SWE environment before the +agent launches. It exposes a single ``terminal`` tool over HTTP (streamable +HTTP MCP transport) that the agent discovers via its MCP config. + +The ``terminal`` tool supports two call shapes: + + terminal(command="git diff HEAD") + Execute a shell command in the workspace and return output. + + terminal(final_answer="I fixed the bug by ...") + Signal that the agent is done. The server runs the pre-configured + verify commands, computes a reward, writes it to the reward file, + and returns the verification outcome. + +Configuration is read from a JSON file whose path is given by the +``SWE_MCP_CONFIG`` environment variable (default: +``/home/user/.swe_mcp_config.json``). + +This file is stdlib-only so it works in bare sandbox images (no pip deps). +""" + +from __future__ import annotations + +import json +import os +import subprocess +import sys +import time +from http.server import BaseHTTPRequestHandler, HTTPServer +from typing import Any + +# ── Config ───────────────────────────────────────────────────────────────── + +DEFAULT_CONFIG_PATH = "/home/user/.swe_mcp_config.json" +DEFAULT_PORT = 8765 +DEFAULT_WORKSPACE = "/home/user/workdir" +DEFAULT_COMMAND_TIMEOUT = 300 +DEFAULT_OUTPUT_LIMIT = 16_000 # chars per command output +REWARD_FILE = "/home/user/logs/verifier/reward.txt" +FINAL_ANSWER_FILE = "/home/user/logs/agent/final_answer.txt" +DONE_MARKER = "/home/user/logs/agent/.done" + +_config: dict[str, Any] = {} +_done = False + + +def load_config() -> dict[str, Any]: + """Load config from the JSON file.""" + path = os.environ.get("SWE_MCP_CONFIG", DEFAULT_CONFIG_PATH) + if os.path.exists(path): + with open(path) as f: + return json.load(f) + return {} + + +def execute_command( + command: str, + workspace: str, + timeout: int, + output_limit: int, +) -> dict[str, Any]: + """Run a shell command and return structured result.""" + t0 = time.time() + try: + proc = subprocess.run( + command, + shell=True, + cwd=workspace, + capture_output=True, + text=True, + timeout=timeout, + ) + return { + "exit_code": proc.returncode, + "stdout": proc.stdout[-output_limit:] if proc.stdout else "", + "stderr": proc.stderr[-output_limit:] if proc.stderr else "", + "duration_s": round(time.time() - t0, 3), + "timed_out": False, + } + except subprocess.TimeoutExpired: + return { + "exit_code": -1, + "stdout": "", + "stderr": f"Command timed out after {timeout}s", + "duration_s": round(time.time() - t0, 3), + "timed_out": True, + } + except Exception as exc: + return { + "exit_code": -1, + "stdout": "", + "stderr": f"{type(exc).__name__}: {exc}", + "duration_s": round(time.time() - t0, 3), + "timed_out": False, + } + + +def run_verify( + verify_commands: list[str], + workspace: str, + timeout: int, + output_limit: int, +) -> tuple[float, list[dict[str, Any]]]: + """Run verify commands and compute reward. + + Returns (reward, results_list). + Reward = passed / total. An explicit reward.txt overrides. + """ + results = [] + passed = 0 + for cmd in verify_commands: + r = execute_command(cmd, workspace, timeout, output_limit) + results.append({"cmd": cmd, **r}) + if r["exit_code"] == 0: + passed += 1 + + # Check for explicit reward override + reward = passed / max(len(verify_commands), 1) + if os.path.exists(REWARD_FILE): + try: + with open(REWARD_FILE) as f: + reward = float(f.read().strip()) + except (ValueError, OSError): + pass + + return reward, results + + +def handle_terminal(arguments: dict[str, Any]) -> dict[str, Any]: + """Core terminal tool logic.""" + global _done + + command = arguments.get("command") + final_answer = arguments.get("final_answer") + + if _done: + return { + "error": "Session is already complete. No further commands accepted.", + "done": True, + } + + if command and final_answer: + return { + "error": "Provide exactly one of 'command' or 'final_answer', not both.", + "done": False, + } + + if not command and not final_answer: + return { + "error": "Provide either 'command' or 'final_answer'.", + "done": False, + } + + workspace = _config.get("workspace", DEFAULT_WORKSPACE) + timeout = _config.get("timeout_per_command_s", DEFAULT_COMMAND_TIMEOUT) + output_limit = _config.get("output_limit", DEFAULT_OUTPUT_LIMIT) + + if command: + result = execute_command(command, workspace, timeout, output_limit) + return { + "output": result["stdout"], + "stderr": result["stderr"], + "exit_code": result["exit_code"], + "timed_out": result["timed_out"], + "done": False, + } + + # final_answer path + _done = True + + # Write final answer + os.makedirs(os.path.dirname(FINAL_ANSWER_FILE), exist_ok=True) + with open(FINAL_ANSWER_FILE, "w") as f: + f.write(final_answer) + + # Write done marker + os.makedirs(os.path.dirname(DONE_MARKER), exist_ok=True) + with open(DONE_MARKER, "w") as f: + f.write("1") + + # Run verify commands + verify_commands = _config.get("verify_commands", []) + if verify_commands: + reward, verify_results = run_verify( + verify_commands, workspace, timeout, output_limit + ) + # Write reward + os.makedirs(os.path.dirname(REWARD_FILE), exist_ok=True) + with open(REWARD_FILE, "w") as f: + f.write(str(reward)) + + return { + "message": "Submission received. Verification complete.", + "reward": reward, + "verify_results": verify_results, + "done": True, + } + + return { + "message": "Submission received. No verify commands configured.", + "reward": None, + "done": True, + } + + +# ── MCP JSON-RPC ────────────────────────────────────────────────────────── + +TERMINAL_TOOL_SCHEMA = { + "name": "terminal", + "description": ( + "Execute a shell command in the repository workspace, or submit " + "your final answer.\n\n" + "Use terminal(command='...') to run any shell command (git, " + "python, cat, grep, etc.).\n\n" + "When you are confident the issue is fixed, call " + "terminal(final_answer='...') to submit. This runs the " + "verification tests and reports the result." + ), + "inputSchema": { + "type": "object", + "properties": { + "command": { + "type": "string", + "description": "Shell command to execute in the workspace.", + }, + "final_answer": { + "type": "string", + "description": ( + "Submit your final answer. Explain what you changed " + "and why. This triggers verification." + ), + }, + }, + }, +} + + +def handle_jsonrpc(request: dict[str, Any]) -> dict[str, Any]: + """Handle one MCP JSON-RPC request.""" + request_id = request.get("id") + method = request.get("method", "") + params = request.get("params", {}) or {} + + if method == "initialize": + return _success( + { + "protocolVersion": "2025-03-26", + "capabilities": {"tools": {"listChanged": False}}, + "serverInfo": {"name": "swe-terminal", "version": "1.0.0"}, + }, + request_id, + ) + + if method == "notifications/initialized": + # Client acknowledgement, no response needed for notifications + return _success({}, request_id) + + if method == "tools/list": + return _success({"tools": [TERMINAL_TOOL_SCHEMA]}, request_id) + + if method == "tools/call": + tool_name = params.get("name", "") + arguments = params.get("arguments", {}) + + if tool_name != "terminal": + return _error(-32601, f"Unknown tool: {tool_name}", request_id) + + result = handle_terminal(arguments) + return _success( + { + "content": [ + {"type": "text", "text": json.dumps(result, indent=2)}, + ], + "isError": "error" in result, + }, + request_id, + ) + + if method == "ping": + return _success({}, request_id) + + return _error(-32601, f"Method not found: {method}", request_id) + + +def _success(result: Any, request_id: Any) -> dict[str, Any]: + return {"jsonrpc": "2.0", "id": request_id, "result": result} + + +def _error(code: int, message: str, request_id: Any) -> dict[str, Any]: + return { + "jsonrpc": "2.0", + "id": request_id, + "error": {"code": code, "message": message}, + } + + +# ── HTTP Server ──────────────────────────────────────────────────────────── + + +class MCPHandler(BaseHTTPRequestHandler): + """Minimal HTTP handler for MCP streamable HTTP transport.""" + + def do_POST(self): + content_length = int(self.headers.get("Content-Length", 0)) + body = self.rfile.read(content_length) + + try: + request = json.loads(body) + except json.JSONDecodeError: + self._send_json( + 400, + _error(-32700, "Parse error", None), + ) + return + + response = handle_jsonrpc(request) + self._send_json(200, response) + + def do_GET(self): + """Health check endpoint.""" + if self.path in ("/health", "/"): + self._send_json(200, {"status": "ok", "tool": "terminal"}) + else: + self.send_error(404) + + def _send_json(self, status: int, data: Any) -> None: + payload = json.dumps(data).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(payload))) + self.end_headers() + self.wfile.write(payload) + + def log_message(self, fmt, *args): + """Suppress noisy per-request logs; write to stderr only.""" + sys.stderr.write(f"[swe-mcp] {fmt % args}\n") + + +# ── Stdio transport ──────────────────────────────────────────────────────── + + +def run_stdio() -> None: + """Run as an MCP stdio server (read JSON-RPC from stdin, write to stdout). + + This is the preferred transport for agent MCP discovery (.mcp.json): + the agent launches this script as a subprocess and communicates over + stdin/stdout. Logs go to stderr. + """ + sys.stderr.write("[swe-mcp] terminal tool server (stdio mode)\n") + sys.stderr.flush() + + for raw_line in sys.stdin: + line = raw_line.strip() + if not line: + continue + try: + request = json.loads(line) + except json.JSONDecodeError: + response = _error(-32700, "Parse error", None) + sys.stdout.write(json.dumps(response) + "\n") + sys.stdout.flush() + continue + + # Notifications (no id) don't need a response. + if request.get("id") is None and request.get("method", "").startswith( + "notifications/" + ): + continue + + response = handle_jsonrpc(request) + sys.stdout.write(json.dumps(response) + "\n") + sys.stdout.flush() + + +def run_http() -> None: + """Run as an HTTP server (MCP streamable HTTP transport).""" + port = _config.get("port", DEFAULT_PORT) + port_env = os.environ.get("SWE_MCP_PORT") + if port_env: + port = int(port_env) + + server = HTTPServer(("127.0.0.1", port), MCPHandler) + sys.stderr.write(f"[swe-mcp] terminal tool server on http://127.0.0.1:{port}\n") + sys.stderr.flush() + + try: + server.serve_forever() + except KeyboardInterrupt: + pass + finally: + server.server_close() + + +def main() -> None: + """Entry point: load config and start in stdio or HTTP mode.""" + global _config + _config = load_config() + + # Ensure log directories exist (best-effort; may fail outside sandbox). + for d in [ + "/home/user/logs/verifier", + "/home/user/logs/agent", + ]: + try: + os.makedirs(d, exist_ok=True) + except OSError: + pass + + if "--stdio" in sys.argv: + run_stdio() + else: + run_http() + + +if __name__ == "__main__": + main() diff --git a/envs/mini_swe_env/server/swe_environment.py b/envs/mini_swe_env/server/swe_environment.py new file mode 100644 index 000000000..d0913f56f --- /dev/null +++ b/envs/mini_swe_env/server/swe_environment.py @@ -0,0 +1,656 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""SWE environment implementation. + +Single MCP tool ``run_swe_rollout`` with the ``SWETask`` shape: + + - ``instance_id`` — SWE-bench Lite instance identifier + - ``repo`` — GitHub ``org/repo`` to clone + - ``base_commit`` — commit to reset the repo to + - ``instruction`` — problem statement for the agent + - ``setup`` — bash commands run BEFORE the agent + - ``verify`` — bash commands run AFTER the agent + +Reward = ``passed_verify_commands / total`` unless a verify command writes +a float to ``/home/user/logs/verifier/reward.txt`` (override). + +The ``terminal`` tool is delivered via an in-sandbox MCP server +(:mod:`sandbox_mcp_server`) started before the agent launches. +""" + +from __future__ import annotations + +import json +import time +from pathlib import Path +from typing import Any, Optional +from uuid import uuid4 + +from fastmcp import FastMCP + +try: + from openenv.core.env_server.mcp_environment import MCPEnvironment + from openenv.core.env_server.types import Action, Observation + + from ..models import SWECommandResult, SWERolloutResult, SWEState + from ..task_loader_swebench_lite import SWETask, validate_swe_task +except ImportError: # pragma: no cover + from models import SWECommandResult, SWERolloutResult, SWEState # type: ignore + from openenv.core.env_server.mcp_environment import MCPEnvironment + from openenv.core.env_server.types import Action, Observation + from task_loader_swebench_lite import SWETask, validate_swe_task # type: ignore + + +# Long timeout for the single MCP tool (sandbox cold-start + agent run + +# verify can take 10-30 min for real SWE tasks). +_RUN_ROLLOUT_TIMEOUT_S = 2400.0 + +# Sandbox filesystem layout. +HOME = "/home/user" +WORKDIR = f"{HOME}/workdir" +REWARD_FILE = f"{HOME}/logs/verifier/reward.txt" +FINAL_ANSWER_FILE = f"{HOME}/logs/agent/final_answer.txt" +DONE_MARKER = f"{HOME}/logs/agent/.done" +MCP_CONFIG_PATH = f"{HOME}/.swe_mcp_config.json" +MCP_SERVER_PATH = f"{HOME}/.swe_mcp_server.py" +MCP_PORT = 8765 +VERIFY_TIMEOUT_S = 300 +SETUP_TIMEOUT_S = 600 + +# Path to the sandbox_mcp_server.py source alongside this module. +_SANDBOX_MCP_SERVER_SOURCE = Path(__file__).parent / "sandbox_mcp_server.py" + +_SUPPORTED_AGENTS = ("pi", "opencode") +_AGENT_LOG_PATHS: dict[str, str] = { + "pi": f"{HOME}/logs/agent/pi.txt", + "opencode": f"{HOME}/logs/agent/opencode.jsonl", +} + + +class SWEEnvironment(MCPEnvironment): + """Per-session SWE environment exposing ``run_swe_rollout`` MCP tool.""" + + SUPPORTS_CONCURRENT_SESSIONS = True + + def __init__(self) -> None: + from openenv.core.harness.agents import get_agent_spec + from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory + + self._get_agent_spec = get_agent_spec + self._CLIAgentSessionFactory = CLIAgentSessionFactory + + self._state = SWEState(episode_id=str(uuid4())) + + mcp = FastMCP("mini_swe_env") + + @mcp.tool + def run_swe_rollout( + # Task fields (match SWETask shape). + instance_id: str = "", + repo: str = "", + base_commit: str = "", + instruction: str = "", + setup: Optional[list[str]] = None, + verify: Optional[list[str]] = None, + timeout_s: int = 1800, + # Agent config. + agent: str = "pi", + base_url: str = "", + api_key: str = "", + model: str = "", + agent_timeout_s: float = 600.0, + # Infrastructure. + sandbox_backend: str = "docker", + sandbox_image: str = "", + task_id: str = "", + task_json: str = "", + ) -> str: + """Run one SWE rollout end-to-end. + + Pass either individual fields (instance_id, repo, ...) or a + complete SWETask as ``task_json``. Returns a JSON-serialized + ``SWERolloutResult``. + """ + return self._run_swe_rollout_impl( + instance_id=instance_id, + repo=repo, + base_commit=base_commit, + instruction=instruction, + setup=list(setup or []), + verify=list(verify or []), + timeout_s=timeout_s, + agent=agent, + base_url=base_url, + api_key=api_key, + model=model, + agent_timeout_s=agent_timeout_s, + sandbox_backend=sandbox_backend, + sandbox_image=sandbox_image, + task_id=task_id, + task_json=task_json, + ) + + super().__init__(mcp) + + # ── OpenEnv lifecycle ────────────────────────────────────────────────── + + def reset( + self, + seed: Optional[int] = None, + episode_id: Optional[str] = None, + **_: Any, + ) -> Observation: + self._state = SWEState(episode_id=episode_id or str(uuid4())) + return Observation( + done=False, + reward=None, + metadata={ + "status": "ready", + "message": ( + "mini_swe_env ready. Call run_swe_rollout(...) with an SWE task." + ), + }, + ) + + def _step_impl( + self, + action: Action, + timeout_s: Optional[float] = None, + **_: Any, + ) -> Observation: + return Observation( + done=False, + reward=None, + metadata={ + "error": ( + f"Unknown action type: {type(action).__name__}. " + "Use CallToolAction(name='run_swe_rollout', ...)." + ), + }, + ) + + def step( + self, + action: Action, + timeout_s: Optional[float] = None, + **kwargs: Any, + ) -> Observation: + if timeout_s is None: + timeout_s = _RUN_ROLLOUT_TIMEOUT_S + return super().step(action, timeout_s=timeout_s, **kwargs) + + async def step_async( + self, + action: Action, + timeout_s: Optional[float] = None, + **kwargs: Any, + ) -> Observation: + if timeout_s is None: + timeout_s = _RUN_ROLLOUT_TIMEOUT_S + return await super().step_async(action, timeout_s=timeout_s, **kwargs) + + @property + def state(self) -> Any: + return self._state + + # ── Rollout orchestration ────────────────────────────────────────────── + + def _run_swe_rollout_impl( + self, + *, + instance_id: str, + repo: str, + base_commit: str, + instruction: str, + setup: list[str], + verify: list[str], + timeout_s: int, + agent: str, + base_url: str, + api_key: str, + model: str, + agent_timeout_s: float, + sandbox_backend: str, + sandbox_image: str, + task_id: str, + task_json: str, + ) -> str: + result = SWERolloutResult(task_id=task_id) + t0 = time.time() + + # ── Resolve task ────────────────────────────────────────────── + task = self._resolve_task( + instance_id=instance_id, + repo=repo, + base_commit=base_commit, + instruction=instruction, + setup=setup, + verify=verify, + timeout_s=timeout_s, + task_json=task_json, + task_id=task_id, + ) + if isinstance(task, str): + # Error string + result.error = task + result.wall_s = round(time.time() - t0, 3) + return result.model_dump_json() + + result.task_id = task.task_id + result.instance_id = task.instance_id + + # ── Validate agent + LLM config ─────────────────────────────── + agent = (agent or "pi").strip() + if agent not in _SUPPORTED_AGENTS: + result.error = ( + f"Unsupported agent {agent!r}; supported: {_SUPPORTED_AGENTS}" + ) + result.wall_s = round(time.time() - t0, 3) + return result.model_dump_json() + + if not (base_url and api_key and model): + result.error = "Must provide base_url, api_key, and model." + result.wall_s = round(time.time() - t0, 3) + return result.model_dump_json() + + # ── Create sandbox ──────────────────────────────────────────── + sandbox = None + session = None + try: + backend = self._create_backend( + sandbox_backend, sandbox_image or task.sandbox_image + ) + sandbox = backend.create( + timeout_s=int(agent_timeout_s) + 600, + ) + result.sandbox_id = sandbox.sandbox_id + + # ── Stage repo ──────────────────────────────────────────── + self._stage_repo(sandbox, task) + + # ── Run setup commands ──────────────────────────────────── + for cmd in task.setup: + cr = self._exec_command(sandbox, cmd, cwd=WORKDIR) + result.setup_results.append(cr) + if cr.exit_code != 0: + result.error = f"Setup failed (exit {cr.exit_code}): {cmd[:120]}" + break + + if result.error is not None: + result.wall_s = round(time.time() - t0, 3) + return result.model_dump_json() + + # ── Start in-sandbox MCP server ─────────────────────────── + self._deploy_mcp_server(sandbox, task) + + # ── Launch agent ────────────────────────────────────────── + spec = self._get_agent_spec(agent) + config = self._build_agent_config( + agent=agent, + base_url=base_url, + api_key=api_key, + model=model, + agent_timeout_s=agent_timeout_s, + ) + rollout_task = self._build_agent_task(task) + + # Use the already-created sandbox rather than creating a new one. + # We build the session manually using the driver's helpers. + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + driver = CLIAgentDriver( + spec=spec, + sandbox_backend=backend, + mode="black_box", + ) + # Agent install + file upload (instruction, mcp config, etc.) + driver._bootstrap_sandbox(sandbox, rollout_task, config) + agent_bg = driver._start_agent(sandbox, rollout_task, config) + + from openenv.core.harness.agents.cli_driver import CLIAgentSession + + session = CLIAgentSession( + spec=spec, + sandbox=sandbox, + task=rollout_task, + config=config, + agent_bg_job=agent_bg, + ) + + # ── Wait for agent ──────────────────────────────────────── + try: + result.agent_exit_code = session.wait_for_completion( + timeout_s=agent_timeout_s + ) + except TimeoutError as exc: + result.error = f"Agent timeout: {exc}" + + # ── Verify ──────────────────────────────────────────────── + verify_passed = 0 + for cmd in task.verify: + cr = self._exec_command(sandbox, cmd, cwd=WORKDIR) + result.verify_results.append(cr) + if cr.exit_code == 0: + verify_passed += 1 + + # ── Reward ──────────────────────────────────────────────── + override = self._read_reward(sandbox) + if override is not None: + result.reward = override + elif task.verify: + result.reward = verify_passed / len(task.verify) + else: + result.reward = None + + # ── Collect artifacts ────────────────────────────────────── + result.files, result.files_extra = self._collect_files(sandbox) + result.agent_log_tail = self._collect_agent_log(sandbox, session, agent) + + except Exception as exc: # noqa: BLE001 + result.error = f"{type(exc).__name__}: {exc}" + if sandbox is not None: + result.agent_log_tail = self._safe_read( + sandbox, _AGENT_LOG_PATHS.get(agent, "") + )[-2000:] + finally: + if session is not None: + try: + session.close() + except Exception: + pass + elif sandbox is not None: + try: + sandbox.kill() + except Exception: + pass + + result.wall_s = round(time.time() - t0, 3) + + # ── Update state ────────────────────────────────────────────── + self._state.rollouts_completed += 1 + self._state.last_reward = result.reward + self._state.last_task_id = result.task_id or None + self._state.last_instance_id = result.instance_id or None + self._state.last_sandbox_id = result.sandbox_id or None + + return result.model_dump_json() + + # ── Task resolution ──────────────────────────────────────────────────── + + def _resolve_task( + self, + *, + instance_id: str, + repo: str, + base_commit: str, + instruction: str, + setup: list[str], + verify: list[str], + timeout_s: int, + task_json: str, + task_id: str, + ) -> SWETask | str: + """Build an SWETask from the provided arguments. + + Returns the task on success, or an error string on failure. + """ + if task_json: + try: + raw = json.loads(task_json) + task = SWETask(**raw) + validate_swe_task(task) + return task + except Exception as exc: + return f"Invalid task_json: {exc}" + + if not instruction: + return "instruction is required" + if not repo: + return "repo is required" + if not base_commit: + return "base_commit is required" + if not instance_id: + instance_id = f"manual::{repo}::{base_commit[:12]}" + + try: + task = SWETask( + task_id=task_id or f"swebench_lite::{instance_id}", + source="swebench_lite", + instance_id=instance_id, + repo=repo, + base_commit=base_commit, + instruction=instruction, + setup=setup, + verify=verify, + timeout_s=timeout_s, + ) + validate_swe_task(task) + return task + except Exception as exc: + return f"Task validation failed: {exc}" + + # ── Sandbox helpers ──────────────────────────────────────────────────── + + def _create_backend(self, backend_name: str, image: str | None) -> Any: + """Create a sandbox backend by name.""" + from openenv.core.harness.sandbox import create_sandbox_backend + + kwargs: dict[str, Any] = {} + if image: + kwargs["image"] = image + return create_sandbox_backend(backend_name, **kwargs) + + def _stage_repo(self, sandbox: Any, task: SWETask) -> None: + """Clone the repo and reset to base_commit in the sandbox.""" + sandbox.exec(f"mkdir -p {WORKDIR}", timeout=10) + + # Clone repo + clone_url = f"https://github.com/{task.repo}.git" + r = sandbox.exec( + f"git clone --quiet {clone_url} {WORKDIR}", + timeout=SETUP_TIMEOUT_S, + ) + if r.exit_code != 0: + raise RuntimeError( + f"git clone failed (exit {r.exit_code}): {r.stderr[:500]}" + ) + + # Reset to base commit + r = sandbox.exec( + f"git checkout --quiet {task.base_commit}", + cwd=WORKDIR, + timeout=60, + ) + if r.exit_code != 0: + raise RuntimeError( + f"git checkout failed (exit {r.exit_code}): {r.stderr[:500]}" + ) + + def _deploy_mcp_server(self, sandbox: Any, task: SWETask) -> None: + """Write the MCP server script and config into the sandbox, then start it.""" + # Write the MCP server script + mcp_source = _SANDBOX_MCP_SERVER_SOURCE.read_text() + sandbox.write_text(MCP_SERVER_PATH, mcp_source) + + # Write the config + mcp_config = json.dumps( + { + "workspace": WORKDIR, + "verify_commands": list(task.verify), + "timeout_per_command_s": VERIFY_TIMEOUT_S, + "output_limit": 16_000, + "port": MCP_PORT, + }, + indent=2, + ) + sandbox.write_text(MCP_CONFIG_PATH, mcp_config) + + # Ensure log dirs exist + sandbox.exec( + f"mkdir -p {HOME}/logs/verifier {HOME}/logs/agent", + timeout=10, + ) + + # Start the MCP server as a background process + sandbox.start_bg( + f"python3 {MCP_SERVER_PATH}", + envs={"SWE_MCP_CONFIG": MCP_CONFIG_PATH}, + ) + + # Wait for server to be ready + for attempt in range(10): + r = sandbox.exec( + f"curl -sf http://127.0.0.1:{MCP_PORT}/health 2>/dev/null || echo FAIL", + timeout=5, + ) + if "FAIL" not in (r.stdout or ""): + return + time.sleep(0.5) + + raise RuntimeError("In-sandbox MCP server did not start within 5s") + + def _build_agent_config( + self, + *, + agent: str, + base_url: str, + api_key: str, + model: str, + agent_timeout_s: float, + ) -> Any: + """Build the agent-specific config dataclass.""" + from dataclasses import dataclass + + @dataclass + class _AgentConfig: + base_url: str = base_url + api_key: str = api_key + model: str = model + agent_timeout_s: float = agent_timeout_s + sandbox_home: str = HOME + provider: str = "" + thinking: str = "off" + + config = _AgentConfig() + + if agent == "pi": + config.provider = self._infer_provider(base_url) + + return config + + def _build_agent_task(self, task: SWETask) -> Any: + """Build a task object compatible with CLIAgentDriver.""" + from dataclasses import dataclass, field as dc_field + + @dataclass + class _AgentTask: + instruction: str = task.instruction + setup_shell: str | None = None + upload_files: dict[str, str] = dc_field(default_factory=dict) + metadata: dict[str, Any] = dc_field(default_factory=dict) + + return _AgentTask( + metadata={ + "task_id": task.task_id, + "instance_id": task.instance_id, + "repo": task.repo, + }, + ) + + @staticmethod + def _infer_provider(base_url: str) -> str: + url = (base_url or "").lower() + if "router.huggingface.co" in url: + return "huggingface" + if "anthropic" in url: + return "anthropic" + if "googleapis.com" in url or "generativelanguage" in url: + return "gemini" + return "openai" + + def _exec_command( + self, sandbox: Any, cmd: str, cwd: str | None = None + ) -> SWECommandResult: + """Execute a command and return a structured result.""" + t = time.time() + try: + kwargs: dict[str, Any] = {"timeout": VERIFY_TIMEOUT_S} + if cwd: + kwargs["cwd"] = cwd + r = sandbox.exec(cmd, **kwargs) + return SWECommandResult( + cmd=cmd, + exit_code=int(r.exit_code), + stdout=(r.stdout or "")[-4000:], + stderr=(r.stderr or "")[-4000:], + duration_s=round(time.time() - t, 3), + ) + except Exception as exc: # noqa: BLE001 + return SWECommandResult( + cmd=cmd, + exit_code=-1, + stderr=f"{type(exc).__name__}: {exc}", + duration_s=round(time.time() - t, 3), + ) + + def _read_reward(self, sandbox: Any) -> float | None: + """Read explicit reward override from the sandbox.""" + raw = self._safe_read(sandbox, REWARD_FILE).strip() + if not raw: + return None + try: + return float(raw) + except ValueError: + return None + + def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: + """Collect modified files from the workspace.""" + # Use git diff to find changed files (more relevant than a blind find) + listing = sandbox.exec( + f"cd {WORKDIR} && git diff --name-only HEAD 2>/dev/null | head -32", + timeout=10, + ) + files: dict[str, str] = {} + extras: list[str] = [] + for line in (listing.stdout or "").splitlines(): + rel_path = line.strip() + if not rel_path: + continue + full_path = f"{WORKDIR}/{rel_path}" + try: + content = sandbox.read_text(full_path) + if len(content) <= 16_000: + files[rel_path] = content + else: + files[rel_path] = content[:16_000] + "\n... [truncated]" + except Exception: + extras.append(rel_path) + return files, extras + + def _collect_agent_log(self, sandbox: Any, session: Any, agent: str) -> str: + """Collect agent log tail.""" + if session is not None and hasattr(session, "collect_artifacts"): + try: + artifacts = session.collect_artifacts() + if isinstance(artifacts, dict) and "agent_log" in artifacts: + val = artifacts["agent_log"] + if isinstance(val, str): + return val[-4000:] + return json.dumps(val, default=str)[-4000:] + except Exception: + pass + path = _AGENT_LOG_PATHS.get(agent, "") + return self._safe_read(sandbox, path)[-4000:] + + @staticmethod + def _safe_read(sandbox: Any, path: str) -> str: + if not path: + return "" + try: + return sandbox.read_text(path) or "" + except Exception: + return "" From 4b115a802fd8971f5886abaaed14adb56afbef6a Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 13:50:24 +0530 Subject: [PATCH 18/79] refactor: task adapter --- envs/mini_swe_env/README.md | 33 +- envs/mini_swe_env/__init__.py | 39 +- envs/mini_swe_env/models.py | 110 +++- envs/mini_swe_env/server/swe_environment.py | 12 +- .../mini_swe_env/task_loader_swebench_lite.py | 515 ------------------ .../mini_swe_env/tasks/mini_swe_eval.jsonl | 4 - .../mini_swe_env/tasks/mini_swe_train.jsonl | 16 - scripts/swe/build_mini_subset.py | 223 -------- tests/envs/test_swe_task_loader.py | 136 ----- 9 files changed, 125 insertions(+), 963 deletions(-) delete mode 100644 envs/mini_swe_env/task_loader_swebench_lite.py delete mode 100644 examples/mini_swe_env/tasks/mini_swe_eval.jsonl delete mode 100644 examples/mini_swe_env/tasks/mini_swe_train.jsonl delete mode 100644 scripts/swe/build_mini_subset.py delete mode 100644 tests/envs/test_swe_task_loader.py diff --git a/envs/mini_swe_env/README.md b/envs/mini_swe_env/README.md index 8991c66f1..948c7ee9c 100644 --- a/envs/mini_swe_env/README.md +++ b/envs/mini_swe_env/README.md @@ -1,6 +1,6 @@ # Mini SWE Environment -A training environment for SWE-bench Lite tasks using OpenEnv. +A training environment for SWE tasks using OpenEnv. ## Overview @@ -75,27 +75,6 @@ with MiniSWEEnv(base_url="http://localhost:8000") as env: print(f"Files changed: {list(result.files.keys())}") ``` -### From a Task File - -```python -from mini_swe_env import MiniSWEEnv, load_task_file -import json - -tasks = load_task_file("examples/mini_swe_env/tasks/mini_swe_train.jsonl") - -with MiniSWEEnv(base_url="http://localhost:8000") as env: - env.reset() - for task in tasks: - result = env.run_swe_rollout( - task_json=json.dumps(task.to_dict()), - agent="pi", - base_url="https://api.openai.com/v1", - api_key="sk-...", - model="gpt-4o-mini", - ) - print(f"{task.instance_id}: reward={result.reward}") -``` - ### Running the Server ```bash @@ -112,8 +91,8 @@ PYTHONPATH=src:envs python -m mini_swe_env.server.app @dataclass class SWETask: task_id: str # Unique identifier - source: str # "swebench_lite" - instance_id: str # SWE-bench instance id + source: str # Task source identifier + instance_id: str # SWE-bench / SWE-Gym instance id repo: str # "org/repo" base_commit: str # Git commit hash instruction: str # Problem statement @@ -150,9 +129,9 @@ Returns: `{"reward": 0.75, "verify_results": [...], "done": true}` | File | Purpose | |------|---------| -| `task_loader_swebench_lite.py` | SWE-bench Lite → SWETask adapter | -| `models.py` | Pydantic models (SWERolloutResult, SWEState) | +| `models.py` | SWETask dataclass + Pydantic models (SWERolloutResult, SWEState) | | `server/swe_environment.py` | Main environment (MCPEnvironment) | | `server/sandbox_mcp_server.py` | In-sandbox terminal tool server | | `server/app.py` | FastAPI application | -| `client.py` | Typed MiniSWEEnv client | \ No newline at end of file +| `client.py` | Typed MiniSWEEnv client | +| `harness.py` | SWESession + SWESessionFactory for agent harness | diff --git a/envs/mini_swe_env/__init__.py b/envs/mini_swe_env/__init__.py index 43535d50f..e1ef9deae 100644 --- a/envs/mini_swe_env/__init__.py +++ b/envs/mini_swe_env/__init__.py @@ -1,51 +1,32 @@ -"""Mini SWE environment — SWE-bench task adapter + environment. +"""Mini SWE environment — task models + environment. Exports: - Task adapter (Phase 2): - SWETask, validate_swe_task, adapt_swebench_lite_row, ... + Task models: + SWETask, validate_swe_task, coerce_swe_task, SWETaskValidationError - Environment + client (Phase 3): + Environment + client: SWEEnvironment, MiniSWEEnv, SWERolloutResult, ... """ -from .task_loader_swebench_lite import ( - adapt_swebench_lite_row, - adapt_swebench_lite_rows, - AdaptationSkip, - coerce_swe_task, - deterministic_train_eval_split, - load_task_file, - read_jsonl_rows, - SWEBenchLiteAdapterError, - SWETask, - SWETaskValidationError, - validate_swe_task, - write_tasks_jsonl, -) - from .models import ( SWECommandResult, SWERolloutResult, SWEState, + SWETask, + SWETaskValidationError, + coerce_swe_task, + validate_swe_task, ) from .client import MiniSWEEnv __all__ = [ - # Task adapter (Phase 2) - "AdaptationSkip", - "SWEBenchLiteAdapterError", + # Task models "SWETask", "SWETaskValidationError", - "adapt_swebench_lite_row", - "adapt_swebench_lite_rows", "coerce_swe_task", - "deterministic_train_eval_split", - "load_task_file", - "read_jsonl_rows", "validate_swe_task", - "write_tasks_jsonl", - # Models (Phase 3) + # Models "MiniSWEEnv", "SWECommandResult", "SWERolloutResult", diff --git a/envs/mini_swe_env/models.py b/envs/mini_swe_env/models.py index f79a7a25b..77e027de2 100644 --- a/envs/mini_swe_env/models.py +++ b/envs/mini_swe_env/models.py @@ -4,19 +4,119 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Pydantic models for the mini_swe_env server. - -The server exposes a single MCP tool ``run_swe_rollout`` that takes an -SWE task, runs one agent rollout inside a sandbox, and returns a -JSON-serialized :class:`SWERolloutResult`. +"""Data models for the mini_swe_env. + +Contains: + - ``SWETask`` — frozen dataclass representing one SWE task (repo, commit, + instruction, etc.). Shared by the environment server, harness, and + client layers. + - ``SWERolloutResult`` / ``SWECommandResult`` / ``SWEState`` — Pydantic + models for the server's MCP tool. """ from __future__ import annotations +from dataclasses import asdict, dataclass, field +from typing import Any + from openenv.core.env_server.types import State from pydantic import BaseModel, Field +DEFAULT_TIMEOUT_S = 1800 + + +class SWETaskValidationError(ValueError): + """Raised when a task fails schema validation.""" + + +@dataclass(frozen=True) +class SWETask: + """One SWE task (repo + commit + instruction + verify commands). + + This is the internal task shape shared across the environment, + harness, and client layers. It is backend-agnostic: both + SWE-Gym and any future task sources produce ``SWETask`` instances. + """ + + task_id: str + source: str + instance_id: str + repo: str + base_commit: str + instruction: str + setup: list[str] + verify: list[str] + timeout_s: int = DEFAULT_TIMEOUT_S + sandbox_image: str | None = None + metadata: dict[str, Any] = field(default_factory=dict) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a JSONL-friendly dictionary.""" + return asdict(self) + + +def validate_swe_task(task: SWETask) -> None: + """Validate a ``SWETask`` and raise explicit schema errors.""" + errors: list[str] = [] + + for field_name in ( + "task_id", + "source", + "instance_id", + "repo", + "base_commit", + "instruction", + ): + value = getattr(task, field_name) + if not isinstance(value, str) or not value.strip(): + errors.append(f"{field_name} must be a non-empty string") + + if not isinstance(task.setup, list): + errors.append("setup must be a list[str]") + else: + for idx, command in enumerate(task.setup): + if not isinstance(command, str) or not command.strip(): + errors.append(f"setup[{idx}] must be a non-empty string") + + if not isinstance(task.verify, list): + errors.append("verify must be a list[str]") + elif not task.verify: + errors.append("verify must contain at least one command") + else: + for idx, command in enumerate(task.verify): + if not isinstance(command, str) or not command.strip(): + errors.append(f"verify[{idx}] must be a non-empty string") + + if not isinstance(task.timeout_s, int) or task.timeout_s <= 0: + errors.append("timeout_s must be a positive int") + + if task.sandbox_image is not None and ( + not isinstance(task.sandbox_image, str) or not task.sandbox_image.strip() + ): + errors.append("sandbox_image must be None or a non-empty string") + + if not isinstance(task.metadata, dict): + errors.append("metadata must be a dict") + + if errors: + raise SWETaskValidationError("; ".join(errors)) + + +def coerce_swe_task(value: SWETask | dict[str, Any]) -> SWETask: + """Coerce an input mapping into a validated ``SWETask``.""" + if isinstance(value, SWETask): + validate_swe_task(value) + return value + if not isinstance(value, dict): + raise SWETaskValidationError( + f"Expected SWETask or dict, got {type(value).__name__}" + ) + task = SWETask(**value) + validate_swe_task(task) + return task + + class SWECommandResult(BaseModel): """Outcome of one shell command in setup or verify.""" diff --git a/envs/mini_swe_env/server/swe_environment.py b/envs/mini_swe_env/server/swe_environment.py index d0913f56f..989f038f2 100644 --- a/envs/mini_swe_env/server/swe_environment.py +++ b/envs/mini_swe_env/server/swe_environment.py @@ -36,13 +36,9 @@ from openenv.core.env_server.mcp_environment import MCPEnvironment from openenv.core.env_server.types import Action, Observation - from ..models import SWECommandResult, SWERolloutResult, SWEState - from ..task_loader_swebench_lite import SWETask, validate_swe_task + from ..models import SWECommandResult, SWERolloutResult, SWEState, SWETask, validate_swe_task except ImportError: # pragma: no cover - from models import SWECommandResult, SWERolloutResult, SWEState # type: ignore - from openenv.core.env_server.mcp_environment import MCPEnvironment - from openenv.core.env_server.types import Action, Observation - from task_loader_swebench_lite import SWETask, validate_swe_task # type: ignore + from models import SWECommandResult, SWERolloutResult, SWEState, SWETask, validate_swe_task # type: ignore # Long timeout for the single MCP tool (sandbox cold-start + agent run + @@ -418,8 +414,8 @@ def _resolve_task( try: task = SWETask( - task_id=task_id or f"swebench_lite::{instance_id}", - source="swebench_lite", + task_id=task_id or f"swegym::{instance_id}", + source="swegym", instance_id=instance_id, repo=repo, base_commit=base_commit, diff --git a/envs/mini_swe_env/task_loader_swebench_lite.py b/envs/mini_swe_env/task_loader_swebench_lite.py deleted file mode 100644 index 532339007..000000000 --- a/envs/mini_swe_env/task_loader_swebench_lite.py +++ /dev/null @@ -1,515 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. - -"""Task adapter utilities for SWE-bench Lite. - -Includes: - - normalize dataset rows into ``SWETask`` - - deterministic mini-subset selection and train/eval split - - explicit schema validation and skip reasons -""" - -from __future__ import annotations - -import hashlib -import json -import re -import shlex -from dataclasses import asdict, dataclass, field -from pathlib import Path -from typing import Any, Iterable, Sequence - -SOURCE_NAME = "swebench_lite" -DEFAULT_TIMEOUT_S = 1800 - - -class SWETaskValidationError(ValueError): - """Raised when a task fails schema validation.""" - - -class SWEBenchLiteAdapterError(ValueError): - """Raised when a SWE-bench Lite row cannot be adapted.""" - - -@dataclass(frozen=True) -class SWETask: - task_id: str - source: str - instance_id: str - repo: str - base_commit: str - instruction: str - setup: list[str] - verify: list[str] - timeout_s: int = DEFAULT_TIMEOUT_S - sandbox_image: str | None = None - metadata: dict[str, Any] = field(default_factory=dict) - - def to_dict(self) -> dict[str, Any]: - """Serialize to a JSONL-friendly dictionary.""" - return asdict(self) - - -@dataclass(frozen=True) -class AdaptationSkip: - row_index: int - reason: str - instance_id: str | None = None - - -def validate_swe_task(task: SWETask) -> None: - """Validate a ``SWETask`` and raise explicit schema errors.""" - errors: list[str] = [] - - for field_name in ( - "task_id", - "source", - "instance_id", - "repo", - "base_commit", - "instruction", - ): - value = getattr(task, field_name) - if not isinstance(value, str) or not value.strip(): - errors.append(f"{field_name} must be a non-empty string") - - if not isinstance(task.setup, list): - errors.append("setup must be a list[str]") - else: - for idx, command in enumerate(task.setup): - if not isinstance(command, str) or not command.strip(): - errors.append(f"setup[{idx}] must be a non-empty string") - - if not isinstance(task.verify, list): - errors.append("verify must be a list[str]") - elif not task.verify: - errors.append("verify must contain at least one command") - else: - for idx, command in enumerate(task.verify): - if not isinstance(command, str) or not command.strip(): - errors.append(f"verify[{idx}] must be a non-empty string") - - if not isinstance(task.timeout_s, int) or task.timeout_s <= 0: - errors.append("timeout_s must be a positive int") - - if task.sandbox_image is not None and ( - not isinstance(task.sandbox_image, str) or not task.sandbox_image.strip() - ): - errors.append("sandbox_image must be None or a non-empty string") - - if not isinstance(task.metadata, dict): - errors.append("metadata must be a dict") - - if errors: - raise SWETaskValidationError("; ".join(errors)) - - -def coerce_swe_task(value: SWETask | dict[str, Any]) -> SWETask: - """Coerce an input mapping into a validated ``SWETask``.""" - if isinstance(value, SWETask): - validate_swe_task(value) - return value - if not isinstance(value, dict): - raise SWETaskValidationError( - f"Expected SWETask or dict, got {type(value).__name__}" - ) - task = SWETask(**value) - validate_swe_task(task) - return task - - -def adapt_swebench_lite_row(row: dict[str, Any]) -> SWETask: - """Convert one SWE-bench Lite row into the internal ``SWETask`` shape.""" - if not isinstance(row, dict): - raise SWEBenchLiteAdapterError(f"row must be a dict, got {type(row).__name__}") - - instance_id = _pick_str(row, "instance_id", required=True) - repo = _pick_str(row, "repo", "repository", required=True) - base_commit = _pick_str( - row, - "base_commit", - "commit", - "base_sha", - "environment_setup_commit", - required=True, - ) - instruction = _pick_str( - row, - "instruction", - "problem_statement", - "prompt", - required=True, - ) - - setup = _pick_commands( - row, - "setup", - "setup_commands", - "setup_script", - "setup_scripts", - ) - verify = _pick_commands( - row, - "verify", - "verify_commands", - "evaluation_commands", - "test_commands", - ) - - if not verify: - verify = _derive_verify_from_test_lists(row) - - timeout_s = _coerce_positive_int(row.get("timeout_s"), default=DEFAULT_TIMEOUT_S) - sandbox_image = _pick_optional_str(row, "sandbox_image", "image") - - task_id = _pick_optional_str(row, "task_id") or f"{SOURCE_NAME}::{instance_id}" - - fail_to_pass = _parse_json_string_list( - row.get("FAIL_TO_PASS", row.get("fail_to_pass")) - ) - pass_to_pass = _parse_json_string_list( - row.get("PASS_TO_PASS", row.get("pass_to_pass")) - ) - hints_text = _pick_optional_str(row, "hints_text") - - metadata: dict[str, Any] = { - "dataset": SOURCE_NAME, - "created_at": _pick_optional_str(row, "created_at"), - "version": _pick_optional_str(row, "version"), - "fail_to_pass_count": len(fail_to_pass), - "pass_to_pass_count": len(pass_to_pass), - } - if hints_text: - # Keep metadata compact for JSONL task files. - metadata["hints_preview"] = hints_text[:280] - metadata = {k: v for k, v in metadata.items() if v not in (None, "")} - - task = SWETask( - task_id=task_id, - source=SOURCE_NAME, - instance_id=instance_id, - repo=repo, - base_commit=base_commit, - instruction=instruction, - setup=setup, - verify=verify, - timeout_s=timeout_s, - sandbox_image=sandbox_image, - metadata=metadata, - ) - - try: - validate_swe_task(task) - except SWETaskValidationError as exc: - raise SWEBenchLiteAdapterError( - f"row {instance_id!r} failed schema validation: {exc}" - ) from exc - - return task - - -def adapt_swebench_lite_rows( - rows: Iterable[dict[str, Any]], *, strict: bool = False -) -> tuple[list[SWETask], list[AdaptationSkip]]: - """Adapt an iterable of rows. - - When ``strict=False`` invalid rows are skipped and reported in ``AdaptationSkip``. - When ``strict=True`` the first adapter error is raised. - """ - tasks: list[SWETask] = [] - skipped: list[AdaptationSkip] = [] - - for index, row in enumerate(rows, start=1): - try: - task = adapt_swebench_lite_row(row) - tasks.append(task) - except SWEBenchLiteAdapterError as exc: - if strict: - raise - instance_id = None - if isinstance(row, dict): - maybe_id = row.get("instance_id") - instance_id = str(maybe_id) if maybe_id else None - skipped.append( - AdaptationSkip( - row_index=index, reason=str(exc), instance_id=instance_id - ) - ) - - return tasks, skipped - - -def read_jsonl_rows(path: str | Path) -> list[dict[str, Any]]: - """Read newline-delimited JSON rows from ``path``.""" - file_path = Path(path) - rows: list[dict[str, Any]] = [] - for line_no, raw_line in enumerate(file_path.read_text().splitlines(), start=1): - line = raw_line.strip() - if not line: - continue - try: - payload = json.loads(line) - except json.JSONDecodeError as exc: - raise SWEBenchLiteAdapterError( - f"invalid JSON on line {line_no}: {exc.msg}" - ) from exc - if not isinstance(payload, dict): - raise SWEBenchLiteAdapterError( - f"line {line_no} must decode to an object, got {type(payload).__name__}" - ) - rows.append(payload) - return rows - - -def write_tasks_jsonl(path: str | Path, tasks: Sequence[SWETask]) -> None: - """Write validated tasks to JSONL.""" - out_path = Path(path) - out_path.parent.mkdir(parents=True, exist_ok=True) - - lines: list[str] = [] - for task in tasks: - validate_swe_task(task) - lines.append(json.dumps(task.to_dict(), sort_keys=True)) - out_path.write_text("\n".join(lines) + ("\n" if lines else "")) - - -def deterministic_train_eval_split( - tasks: Sequence[SWETask], - *, - subset_size: int = 20, - train_size: int = 16, - seed: int = 17, -) -> tuple[list[SWETask], list[SWETask]]: - """Create a deterministic subset and split without relying on RNG internals.""" - if subset_size <= 0: - raise ValueError("subset_size must be > 0") - if train_size <= 0: - raise ValueError("train_size must be > 0") - if train_size >= subset_size: - raise ValueError("train_size must be < subset_size") - - deduped: dict[str, SWETask] = {} - for task in tasks: - validate_swe_task(task) - deduped[task.task_id] = task - - unique_tasks = list(deduped.values()) - if len(unique_tasks) < subset_size: - raise ValueError( - f"need at least {subset_size} valid unique tasks, got {len(unique_tasks)}" - ) - - ranked = sorted( - unique_tasks, - key=lambda task: (_stable_seeded_rank(task.task_id, seed), task.task_id), - ) - selected = ranked[:subset_size] - return selected[:train_size], selected[train_size:] - - -def load_task_file(path: str | Path) -> list[SWETask]: - """Load and validate existing task JSONL file.""" - return [coerce_swe_task(row) for row in read_jsonl_rows(path)] - - -def _stable_seeded_rank(task_id: str, seed: int) -> str: - digest = hashlib.sha256(f"{seed}:{task_id}".encode("utf-8")).hexdigest() - return digest - - -def _pick_str(row: dict[str, Any], *keys: str, required: bool = False) -> str: - value = _pick_optional_str(row, *keys) - if value is not None: - return value - if required: - key_list = ", ".join(keys) - raise SWEBenchLiteAdapterError(f"missing required field(s): {key_list}") - return "" - - -def _pick_optional_str(row: dict[str, Any], *keys: str) -> str | None: - for key in keys: - if key not in row: - continue - value = row.get(key) - if value is None: - continue - if isinstance(value, str): - normalized = value.strip() - if normalized: - return normalized - continue - normalized = str(value).strip() - if normalized: - return normalized - return None - - -def _coerce_positive_int(value: Any, *, default: int) -> int: - if value is None: - return default - try: - parsed = int(value) - except Exception as exc: - raise SWEBenchLiteAdapterError( - f"timeout_s must be an int, got {value!r}" - ) from exc - if parsed <= 0: - raise SWEBenchLiteAdapterError("timeout_s must be > 0") - return parsed - - -def _pick_commands(row: dict[str, Any], *keys: str) -> list[str]: - for key in keys: - if key in row: - return _coerce_commands(row.get(key), field_name=key) - return [] - - -def _coerce_commands(value: Any, *, field_name: str) -> list[str]: - if value is None: - return [] - - if isinstance(value, str): - stripped = value.strip() - if not stripped: - return [] - if stripped.startswith("[") and stripped.endswith("]"): - try: - parsed = json.loads(stripped) - except json.JSONDecodeError: - parsed = None - else: - return _coerce_commands(parsed, field_name=field_name) - return [line.strip() for line in stripped.splitlines() if line.strip()] - - if isinstance(value, Sequence) and not isinstance( - value, (bytes, bytearray, memoryview) - ): - commands: list[str] = [] - for item in value: - if item is None: - continue - if not isinstance(item, str): - item = str(item) - normalized = item.strip() - if not normalized: - continue - commands.append(normalized) - return commands - - raise SWEBenchLiteAdapterError( - f"{field_name} must be a string or list of strings, got {type(value).__name__}" - ) - - -def _derive_verify_from_test_lists(row: dict[str, Any]) -> list[str]: - candidates = _parse_json_string_list( - row.get("FAIL_TO_PASS", row.get("fail_to_pass")) - ) - if not candidates: - candidates = _parse_json_string_list( - row.get("PASS_TO_PASS", row.get("pass_to_pass")) - ) - - commands: list[str] = [] - for item in candidates: - normalized = str(item).strip() - if not normalized: - continue - if _looks_like_shell_command(normalized): - commands.append(normalized) - continue - - converted_unittest_name = _convert_unittest_style_test_name(normalized) - if converted_unittest_name is not None: - commands.append( - f"python -m pytest -q {shlex.quote(converted_unittest_name)}" - ) - continue - - if _looks_like_pytest_nodeid(normalized): - commands.append(f"python -m pytest -q {shlex.quote(normalized)}") - else: - commands.append(f"python -m pytest -q -k {shlex.quote(normalized)}") - return commands - - -def _parse_json_string_list(value: Any) -> list[str]: - if value is None: - return [] - if isinstance(value, list): - return [str(item) for item in value if str(item).strip()] - if isinstance(value, str): - stripped = value.strip() - if not stripped: - return [] - try: - parsed = json.loads(stripped) - except json.JSONDecodeError: - return [stripped] - if isinstance(parsed, list): - return [str(item) for item in parsed if str(item).strip()] - return [str(parsed)] - return [str(value)] - - -def _looks_like_shell_command(value: str) -> bool: - starts = ( - "pytest ", - "python ", - "python3 ", - "tox ", - "nox ", - "bash ", - "./", - "sh ", - ) - if value.startswith(starts): - return True - return any(token in value for token in (" && ", ";", "|", " > ", " < ")) - - -def _looks_like_pytest_nodeid(value: str) -> bool: - return "::" in value or value.endswith(".py") or "/" in value - - -def _convert_unittest_style_test_name(value: str) -> str | None: - # Example: "test_name (pkg.subpkg.module.TestClass)" - match = re.match(r"^([^\s(]+)\s+\(([^()]+)\)$", value) - if not match: - return None - - test_name = match.group(1).strip() - location = match.group(2).strip() - if not test_name or not location: - return None - - parts = [part for part in location.split(".") if part] - if len(parts) < 2: - return None - - class_name = parts[-1] - module_path = "/".join(parts[:-1]) + ".py" - return f"{module_path}::{class_name}::{test_name}" - - -__all__ = [ - "AdaptationSkip", - "DEFAULT_TIMEOUT_S", - "SOURCE_NAME", - "SWEBenchLiteAdapterError", - "SWETask", - "SWETaskValidationError", - "adapt_swebench_lite_row", - "adapt_swebench_lite_rows", - "coerce_swe_task", - "deterministic_train_eval_split", - "load_task_file", - "read_jsonl_rows", - "validate_swe_task", - "write_tasks_jsonl", -] diff --git a/examples/mini_swe_env/tasks/mini_swe_eval.jsonl b/examples/mini_swe_env/tasks/mini_swe_eval.jsonl deleted file mode 100644 index ff77845b1..000000000 --- a/examples/mini_swe_env/tasks/mini_swe_eval.jsonl +++ /dev/null @@ -1,4 +0,0 @@ -{"base_commit": "3ed7590ed411bd93b26098faab4f23619cdb2267", "instance_id": "sphinx-doc__sphinx-8713", "instruction": "napoleon_use_param should also affect \"other parameters\" section\nSubject: napoleon_use_param should also affect \"other parameters\" section\r\n\r\n### Problem\r\nCurrently, napoleon always renders the Other parameters section as if napoleon_use_param was False, see source\r\n```\r\n def _parse_other_parameters_section(self, section):\r\n # type: (unicode) -> List[unicode]\r\n return self._format_fields(_('Other Parameters'), self._consume_fields())\r\n\r\n def _parse_parameters_section(self, section):\r\n # type: (unicode) -> List[unicode]\r\n fields = self._consume_fields()\r\n if self._config.napoleon_use_param:\r\n return self._format_docutils_params(fields)\r\n else:\r\n return self._format_fields(_('Parameters'), fields)\r\n```\r\nwhereas it would make sense that this section should follow the same formatting rules as the Parameters section.\r\n\r\n#### Procedure to reproduce the problem\r\n```\r\nIn [5]: print(str(sphinx.ext.napoleon.NumpyDocstring(\"\"\"\\ \r\n ...: Parameters \r\n ...: ---------- \r\n ...: x : int \r\n ...: \r\n ...: Other parameters \r\n ...: ---------------- \r\n ...: y: float \r\n ...: \"\"\"))) \r\n:param x:\r\n:type x: int\r\n\r\n:Other Parameters: **y** (*float*)\r\n```\r\n\r\nNote the difference in rendering.\r\n\r\n#### Error logs / results\r\nSee above.\r\n\r\n#### Expected results\r\n```\r\n:param x:\r\n:type x: int\r\n\r\n:Other Parameters: // Or some other kind of heading.\r\n:param: y\r\n:type y: float\r\n```\r\n\r\nAlternatively another separate config value could be introduced, but that seems a bit overkill.\r\n\r\n### Reproducible project / your project\r\nN/A\r\n\r\n### Environment info\r\n- OS: Linux\r\n- Python version: 3.7\r\n- Sphinx version: 1.8.1", "metadata": {"created_at": "2021-01-20T14:24:12Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 45, "version": "4.0"}, "repo": "sphinx-doc/sphinx", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sphinx-doc__sphinx-8713", "timeout_s": 1800, "verify": ["python -m pytest -q tests/test_ext_napoleon_docstring.py::NumpyDocstringTest::test_parameters_with_class_reference"]} -{"base_commit": "d232fd76a85870daf345fd8f8d617fe7802ae194", "instance_id": "django__django-11910", "instruction": "ForeignKey's to_field parameter gets the old field's name when renaming a PrimaryKey.\nDescription\n\t\nHaving these two models \nclass ModelA(models.Model):\n\tfield_wrong = models.CharField('field1', max_length=50, primary_key=True) # I'm a Primary key.\nclass ModelB(models.Model):\n\tfield_fk = models.ForeignKey(ModelA, blank=True, null=True, on_delete=models.CASCADE) \n... migrations applyed ...\nthe ModelA.field_wrong field has been renamed ... and Django recognizes the \"renaming\"\n# Primary key renamed\nclass ModelA(models.Model):\n\tfield_fixed = models.CharField('field1', max_length=50, primary_key=True) # I'm a Primary key.\nAttempts to to_field parameter. \nThe to_field points to the old_name (field_typo) and not to the new one (\"field_fixed\")\nclass Migration(migrations.Migration):\n\tdependencies = [\n\t\t('app1', '0001_initial'),\n\t]\n\toperations = [\n\t\tmigrations.RenameField(\n\t\t\tmodel_name='modela',\n\t\t\told_name='field_wrong',\n\t\t\tnew_name='field_fixed',\n\t\t),\n\t\tmigrations.AlterField(\n\t\t\tmodel_name='modelb',\n\t\t\tname='modela',\n\t\t\tfield=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='app1.ModelB', to_field='field_wrong'),\n\t\t),\n\t]", "metadata": {"created_at": "2019-10-14T01:56:49Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Thanks for this ticket. It looks like a regression in dcdd219ee1e062dc6189f382e0298e0adf5d5ddf, because an AlterField operation wasn't generated in such cases before this change (and I don't think we need it).", "pass_to_pass_count": 110, "version": "3.1"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-11910", "timeout_s": 1800, "verify": ["python -m pytest -q migrations/test_autodetector.py::AutodetectorTests::test_rename_referenced_primary_key"]} -{"base_commit": "06632c0d185128a53c57ccc73b25b6408e90bb89", "instance_id": "scikit-learn__scikit-learn-14983", "instruction": "RepeatedKFold and RepeatedStratifiedKFold do not show correct __repr__ string\n#### Description\r\n\r\n`RepeatedKFold` and `RepeatedStratifiedKFold` do not show correct \\_\\_repr\\_\\_ string.\r\n\r\n#### Steps/Code to Reproduce\r\n\r\n```python\r\n>>> from sklearn.model_selection import RepeatedKFold, RepeatedStratifiedKFold\r\n>>> repr(RepeatedKFold())\r\n>>> repr(RepeatedStratifiedKFold())\r\n```\r\n\r\n#### Expected Results\r\n\r\n```python\r\n>>> repr(RepeatedKFold())\r\nRepeatedKFold(n_splits=5, n_repeats=10, random_state=None)\r\n>>> repr(RepeatedStratifiedKFold())\r\nRepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=None)\r\n```\r\n\r\n#### Actual Results\r\n\r\n```python\r\n>>> repr(RepeatedKFold())\r\n''\r\n>>> repr(RepeatedStratifiedKFold())\r\n''\r\n```\r\n\r\n#### Versions\r\n```\r\nSystem:\r\n python: 3.7.4 (default, Aug 9 2019, 18:34:13) [MSC v.1915 64 bit (AMD64)]\r\nexecutable: D:\\anaconda3\\envs\\xyz\\python.exe\r\n machine: Windows-10-10.0.16299-SP0\r\n\r\nBLAS:\r\n macros:\r\n lib_dirs:\r\ncblas_libs: cblas\r\n\r\nPython deps:\r\n pip: 19.2.2\r\nsetuptools: 41.0.1\r\n sklearn: 0.21.2\r\n numpy: 1.16.4\r\n scipy: 1.3.1\r\n Cython: None\r\n pandas: 0.24.2\r\n```", "metadata": {"created_at": "2019-09-14T15:31:18Z", "dataset": "swebench_lite", "fail_to_pass_count": 2, "hints_preview": "The `__repr__` is not defined in the `_RepeatedSplit` class from which these cross-validation are inheriting. A possible fix should be:\r\n\r\n```diff\r\ndiff --git a/sklearn/model_selection/_split.py b/sklearn/model_selection/_split.py\r\nindex ab681e89c..8a16f68bc 100644\r\n--- a/sklearn", "pass_to_pass_count": 105, "version": "0.22"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-14983", "timeout_s": 1800, "verify": ["python -m pytest -q 'sklearn/model_selection/tests/test_split.py::test_repeated_cv_repr[RepeatedKFold]'", "python -m pytest -q 'sklearn/model_selection/tests/test_split.py::test_repeated_cv_repr[RepeatedStratifiedKFold]'"]} -{"base_commit": "d3b4158dea271485e3daa11bf82e69b8dab348ce", "instance_id": "sympy__sympy-24909", "instruction": "Bug with milli prefix\nWhat happened:\r\n```\r\nIn [1]: from sympy.physics.units import milli, W\r\nIn [2]: milli*W == 1\r\nOut[2]: True\r\nIn [3]: W*milli\r\nOut[3]: watt*Prefix(milli, m, -3, 10)\r\n```\r\nWhat I expected to happen: milli*W should evaluate to milli watts / mW\r\n\r\n`milli*W` or more generally `milli` times some unit evaluates to the number 1. I have tried this with Watts and Volts, I'm not sure what other cases this happens. I'm using sympy version 1.11.1-1 on Arch Linux with Python 3.10.9. If you cannot reproduce I would be happy to be of any assitance.", "metadata": {"created_at": "2023-03-13T14:24:25Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "I get a 1 for all of the following (and some are redundant like \"V\" and \"volt\"):\r\n```python\r\nW, joule, ohm, newton, volt, V, v, volts, henrys, pa, kilogram, ohms, kilograms, Pa, weber, tesla, Wb, H, wb, newtons, kilometers, webers, pascals, kilometer, watt, T, km, kg, joules, pas", "pass_to_pass_count": 2, "version": "1.13"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-24909", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_prefix_operations"]} diff --git a/examples/mini_swe_env/tasks/mini_swe_train.jsonl b/examples/mini_swe_env/tasks/mini_swe_train.jsonl deleted file mode 100644 index 7a4f010f7..000000000 --- a/examples/mini_swe_env/tasks/mini_swe_train.jsonl +++ /dev/null @@ -1,16 +0,0 @@ -{"base_commit": "5c2e1f96a7ff562d4a778f4ca9ffc9c81557197e", "instance_id": "sympy__sympy-11870", "instruction": "simplifying exponential -> trig identities\n```\r\nf = 1 / 2 * (-I*exp(I*k) + I*exp(-I*k))\r\ntrigsimp(f)\r\n```\r\n\r\nIdeally, this would yield `sin(k)`. Is there a way to do this?\r\n\r\nAs a corollary, it would be awesome if \r\n\r\n```\r\nf = 1 / 2 / k* (-I*exp(I*k) + I*exp(-I*k))\r\ntrigsimp(f)\r\n```\r\n\r\ncould yield `sinc(k)`. Thank you for your consideration!", "metadata": {"created_at": "2016-11-17T21:36:03Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "rewrite can be used:\n\n```\n>>> f = S(1) / 2 * (-I*exp(I*k) + I*exp(-I*k))\n>>> f.rewrite(sin).simplify()\nsin(k)\n```\n\nThank you for that suggestion!\n\n> On Nov 17, 2016, at 01:06, Kalevi Suominen notifications@github.com wrote:\n> \n> rewrite can be used:\n> \n> > > > f = S(1) / 2 \\* (-I", "pass_to_pass_count": 58, "version": "1.1"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-11870", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_sinc"]} -{"base_commit": "4964b468c83c06971eb743fbc57cc404f760c573", "instance_id": "pytest-dev__pytest-8365", "instruction": "tmpdir creation fails when the username contains illegal characters for directory names\n`tmpdir`, `tmpdir_factory` and `tmp_path_factory` rely on `getpass.getuser()` for determining the `basetemp` directory. I found that the user name returned by `getpass.getuser()` may return characters that are not allowed for directory names. This may lead to errors while creating the temporary directory.\r\n\r\nThe situation in which I reproduced this issue was while being logged in through an ssh connection into my Windows 10 x64 Enterprise version (1909) using an OpenSSH_for_Windows_7.7p1 server. In this configuration the command `python -c \"import getpass; print(getpass.getuser())\"` returns my domain username e.g. `contoso\\john_doe` instead of `john_doe` as when logged in regularly using a local session.\r\n\r\nWhen trying to create a temp directory in pytest through e.g. `tmpdir_factory.mktemp('foobar')` this fails with the following error message:\r\n```\r\nself = WindowsPath('C:/Users/john_doe/AppData/Local/Temp/pytest-of-contoso/john_doe')\r\nmode = 511, parents = False, exist_ok = True\r\n\r\n def mkdir(self, mode=0o777, parents=False, exist_ok=False):\r\n \"\"\"\r\n Create a new directory at this given path.\r\n \"\"\"\r\n if self._closed:\r\n self._raise_closed()\r\n try:\r\n> self._accessor.mkdir(self, mode)\r\nE FileNotFoundError: [WinError 3] The system cannot find the path specified: 'C:\\\\Users\\\\john_doe\\\\AppData\\\\Local\\\\Temp\\\\pytest-of-contoso\\\\john_doe'\r\n\r\nC:\\Python38\\lib\\pathlib.py:1266: FileNotFoundError\r\n```\r\n\r\nI could also reproduce this without the complicated ssh/windows setup with pytest 6.2.2 using the following commands from a `cmd`:\r\n```bat\r\necho def test_tmpdir(tmpdir):>test_tmp.py\r\necho pass>>test_tmp.py\r\nset LOGNAME=contoso\\john_doe\r\npy.test test_tmp.py\r\n```\r\n\r\nThanks for having a look at this!", "metadata": {"created_at": "2021-02-22T20:26:35Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Thanks for the report @pborsutzki!", "pass_to_pass_count": 32, "version": "6.3"}, "repo": "pytest-dev/pytest", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::pytest-dev__pytest-8365", "timeout_s": 1800, "verify": ["python -m pytest -q testing/test_tmpdir.py::test_tmp_path_factory_handles_invalid_dir_characters"]} -{"base_commit": "4652f1f0aa459a7b980441d629648707c32e36bf", "instance_id": "django__django-12915", "instruction": "Add get_response_async for ASGIStaticFilesHandler\nDescription\n\t\nIt looks like the StaticFilesHandlerMixin is missing the the async response function.\nWithout this, when trying to use the ASGIStaticFilesHandler, this is the traceback:\nException inside application: 'NoneType' object is not callable\nTraceback (most recent call last):\n File \".../lib/python3.7/site-packages/daphne/cli.py\", line 30, in asgi\n\tawait self.app(scope, receive, send)\n File \".../src/django/django/contrib/staticfiles/handlers.py\", line 86, in __call__\n\treturn await super().__call__(scope, receive, send)\n File \".../src/django/django/core/handlers/asgi.py\", line 161, in __call__\n\tresponse = await self.get_response_async(request)\n File \".../src/django/django/core/handlers/base.py\", line 148, in get_response_async\n\tresponse = await self._middleware_chain(request)\nTypeError: 'NoneType' object is not callable", "metadata": {"created_at": "2020-05-14T23:30:01Z", "dataset": "swebench_lite", "fail_to_pass_count": 3, "pass_to_pass_count": 8, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-12915", "timeout_s": 1800, "verify": ["python -m pytest -q staticfiles_tests/test_handlers.py::TestASGIStaticFilesHandler::test_get_async_response", "python -m pytest -q staticfiles_tests/test_handlers.py::TestASGIStaticFilesHandler::test_get_async_response_not_found", "python -m pytest -q asgi/tests.py::ASGITest::test_static_file_response"]} -{"base_commit": "0e987498b00167fdd4a08a41c852a97cb70ce8f2", "instance_id": "sympy__sympy-16106", "instruction": "mathml printer for IndexedBase required\nWriting an `Indexed` object to MathML fails with a `TypeError` exception: `TypeError: 'Indexed' object is not iterable`:\r\n\r\n```\r\nIn [340]: sympy.__version__\r\nOut[340]: '1.0.1.dev'\r\n\r\nIn [341]: from sympy.abc import (a, b)\r\n\r\nIn [342]: sympy.printing.mathml(sympy.IndexedBase(a)[b])\r\n---------------------------------------------------------------------------\r\nTypeError Traceback (most recent call last)\r\n in ()\r\n----> 1 sympy.printing.mathml(sympy.IndexedBase(a)[b])\r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/mathml.py in mathml(expr, **settings)\r\n 442 def mathml(expr, **settings):\r\n 443 \"\"\"Returns the MathML representation of expr\"\"\"\r\n--> 444 return MathMLPrinter(settings).doprint(expr)\r\n 445 \r\n 446 \r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/mathml.py in doprint(self, expr)\r\n 36 Prints the expression as MathML.\r\n 37 \"\"\"\r\n---> 38 mathML = Printer._print(self, expr)\r\n 39 unistr = mathML.toxml()\r\n 40 xmlbstr = unistr.encode('ascii', 'xmlcharrefreplace')\r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/printer.py in _print(self, expr, *args, **kwargs)\r\n 255 printmethod = '_print_' + cls.__name__\r\n 256 if hasattr(self, printmethod):\r\n--> 257 return getattr(self, printmethod)(expr, *args, **kwargs)\r\n 258 # Unknown object, fall back to the emptyPrinter.\r\n 259 return self.emptyPrinter(expr)\r\n\r\n/dev/shm/gerrit/venv/stable-3.5/lib/python3.5/site-packages/sympy/printing/mathml.py in _print_Basic(self, e)\r\n 356 def _print_Basic(self, e):\r\n 357 x = self.dom.createElement(self.mathml_tag(e))\r\n--> 358 for arg in e:\r\n 359 x.appendChild(self._print(arg))\r\n 360 return x\r\n\r\nTypeError: 'Indexed' object is not iterable\r\n```\r\n\r\nIt also fails for more complex expressions where at least one element is Indexed.", "metadata": {"created_at": "2019-02-28T17:21:46Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Now it returns\r\n```\r\n'ab'\r\n```\r\nfor content printer and \r\n```\r\n'indexedindexedbaseab'\r\n```\r\nfor presentation printer", "pass_to_pass_count": 55, "version": "1.4"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-16106", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_print_IndexedBase"]} -{"base_commit": "96a02f3934952d486589dddd3f00b40d5a5ab5f2", "instance_id": "scikit-learn__scikit-learn-11040", "instruction": "Missing parameter validation in Neighbors estimator for float n_neighbors\n```python\r\nfrom sklearn.neighbors import NearestNeighbors\r\nfrom sklearn.datasets import make_blobs\r\nX, y = make_blobs()\r\nneighbors = NearestNeighbors(n_neighbors=3.)\r\nneighbors.fit(X)\r\nneighbors.kneighbors(X)\r\n```\r\n```\r\n~/checkout/scikit-learn/sklearn/neighbors/binary_tree.pxi in sklearn.neighbors.kd_tree.NeighborsHeap.__init__()\r\n\r\nTypeError: 'float' object cannot be interpreted as an integer\r\n```\r\nThis should be caught earlier and a more helpful error message should be raised (or we could be lenient and cast to integer, but I think a better error might be better).\r\n\r\nWe need to make sure that \r\n```python\r\nneighbors.kneighbors(X, n_neighbors=3.)\r\n```\r\nalso works.", "metadata": {"created_at": "2018-04-28T07:18:33Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Hello, I would like to take this as my first issue. \r\nThank you.\n@amueller \r\nI added a simple check for float inputs for n_neighbors in order to throw ValueError if that's the case.\n@urvang96 Did say he was working on it first @Alfo5123 ..\r\n\r\n@amueller I think there is a lot of", "pass_to_pass_count": 44, "version": "0.20"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-11040", "timeout_s": 1800, "verify": ["python -m pytest -q sklearn/neighbors/tests/test_neighbors.py::test_n_neighbors_datatype"]} -{"base_commit": "60dc957a825232fdda9138e2f8878b2ca407a7c9", "instance_id": "django__django-11583", "instruction": "Auto-reloading with StatReloader very intermittently throws \"ValueError: embedded null byte\".\nDescription\n\t\nRaising this mainly so that it's tracked, as I have no idea how to reproduce it, nor why it's happening. It ultimately looks like a problem with Pathlib, which wasn't used prior to 2.2.\nStacktrace:\nTraceback (most recent call last):\n File \"manage.py\" ...\n\texecute_from_command_line(sys.argv)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/__init__.py\", line 381, in execute_from_command_line\n\tutility.execute()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/__init__.py\", line 375, in execute\n\tself.fetch_command(subcommand).run_from_argv(self.argv)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/base.py\", line 323, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 60, in execute\n\tsuper().execute(*args, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/base.py\", line 364, in execute\n\toutput = self.handle(*args, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 95, in handle\n\tself.run(**options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 102, in run\n\tautoreload.run_with_reloader(self.inner_run, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 577, in run_with_reloader\n\tstart_django(reloader, main_func, *args, **kwargs)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 562, in start_django\n\treloader.run(django_main_thread)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 280, in run\n\tself.run_loop()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 286, in run_loop\n\tnext(ticker)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 326, in tick\n\tfor filepath, mtime in self.snapshot_files():\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 342, in snapshot_files\n\tfor file in self.watched_files():\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 241, in watched_files\n\tyield from iter_all_python_module_files()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 103, in iter_all_python_module_files\n\treturn iter_modules_and_files(modules, frozenset(_error_files))\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 132, in iter_modules_and_files\n\tresults.add(path.resolve().absolute())\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 1120, in resolve\n\ts = self._flavour.resolve(self, strict=strict)\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 346, in resolve\n\treturn _resolve(base, str(path)) or sep\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 330, in _resolve\n\ttarget = accessor.readlink(newpath)\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 441, in readlink\n\treturn os.readlink(path)\nValueError: embedded null byte\nI did print(path) before os.readlink(path) in pathlib and ended up with:\n/Users/kez\n/Users/kez/.pyenv\n/Users/kez/.pyenv/versions\n/Users/kez/.pyenv/versions/3.6.2\n/Users/kez/.pyenv/versions/3.6.2/lib\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/asyncio\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/asyncio/selector_events.py\n/Users\nIt always seems to be /Users which is last\nIt may have already printed /Users as part of another .resolve() multiple times (that is, the order is not deterministic, and it may have traversed beyond /Users successfully many times during startup.\nI don't know where to begin looking for the rogue null byte, nor why it only exists sometimes.\nBest guess I have is that there's a mountpoint in /Users to a samba share which may not have been connected to yet? I dunno.\nI have no idea if it's fixable without removing the use of pathlib (which tbh I think should happen anyway, because it's slow) and reverting to using os.path.join and friends. \nI have no idea if it's fixed in a later Python version, but with no easy way to reproduce ... dunno how I'd check.\nI have no idea if it's something specific to my system (pyenv, OSX 10.11, etc)", "metadata": {"created_at": "2019-07-21T20:56:14Z", "dataset": "swebench_lite", "fail_to_pass_count": 2, "hints_preview": "Thanks for the report, however as you've admitted there is too many unknowns to accept this ticket. I don't believe that it is related with pathlib, maybe samba connection is unstable it's hard to tell.\nI don't believe that it is related with pathlib Well ... it definitely is, yo", "pass_to_pass_count": 50, "version": "3.0"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-11583", "timeout_s": 1800, "verify": ["python -m pytest -q utils_tests/test_autoreload.py::TestIterModulesAndFiles::test_path_with_embedded_null_bytes", "python -m pytest -q utils_tests/test_autoreload.py::TestIterModulesAndFiles::test_paths_are_pathlib_instances"]} -{"base_commit": "4a72da71001f154ea60906a2f74898d32b7322a7", "instance_id": "django__django-17087", "instruction": "Class methods from nested classes cannot be used as Field.default.\nDescription\n\t \n\t\t(last modified by Mariusz Felisiak)\n\t \nGiven the following model:\n \nclass Profile(models.Model):\n\tclass Capability(models.TextChoices):\n\t\tBASIC = (\"BASIC\", \"Basic\")\n\t\tPROFESSIONAL = (\"PROFESSIONAL\", \"Professional\")\n\t\t\n\t\t@classmethod\n\t\tdef default(cls) -> list[str]:\n\t\t\treturn [cls.BASIC]\n\tcapabilities = ArrayField(\n\t\tmodels.CharField(choices=Capability.choices, max_length=30, blank=True),\n\t\tnull=True,\n\t\tdefault=Capability.default\n\t)\nThe resulting migration contained the following:\n # ...\n\t migrations.AddField(\n\t\t model_name='profile',\n\t\t name='capabilities',\n\t\t field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, choices=[('BASIC', 'Basic'), ('PROFESSIONAL', 'Professional')], max_length=30), default=appname.models.Capability.default, null=True, size=None),\n\t ),\n # ...\nAs you can see, migrations.AddField is passed as argument \"default\" a wrong value \"appname.models.Capability.default\", which leads to an error when trying to migrate. The right value should be \"appname.models.Profile.Capability.default\".", "metadata": {"created_at": "2023-07-17T20:28:41Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Thanks for the report. It seems that FunctionTypeSerializer should use __qualname__ instead of __name__: django/db/migrations/serializer.py diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index d88cda6e20..06657ebaab 100644 a b class FunctionT", "pass_to_pass_count": 53, "version": "5.0"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-17087", "timeout_s": 1800, "verify": ["python -m pytest -q migrations/test_writer/WriterTests.py::test_serialize_nested_class_method::test_serialize_nested_class_method"]} -{"base_commit": "1c8668b0a021832386470ddf740d834e02c66f69", "instance_id": "scikit-learn__scikit-learn-13142", "instruction": "GaussianMixture predict and fit_predict disagree when n_init>1\n#### Description\r\nWhen `n_init` is specified in GaussianMixture, the results of fit_predict(X) and predict(X) are often different. The `test_gaussian_mixture_fit_predict` unit test doesn't catch this because it does not set `n_init`.\r\n\r\n#### Steps/Code to Reproduce\r\n```\r\npython\r\nfrom sklearn.mixture import GaussianMixture\r\nfrom sklearn.utils.testing import assert_array_equal\r\nimport numpy\r\nX = numpy.random.randn(1000,5)\r\nprint 'no n_init'\r\ngm = GaussianMixture(n_components=5)\r\nc1 = gm.fit_predict(X)\r\nc2 = gm.predict(X)\r\nassert_array_equal(c1,c2)\r\nprint 'n_init=5'\r\ngm = GaussianMixture(n_components=5, n_init=5)\r\nc1 = gm.fit_predict(X)\r\nc2 = gm.predict(X)\r\nassert_array_equal(c1,c2)\r\n```\r\n\r\n#### Expected Results\r\n```\r\nno n_init\r\nn_init=5\r\n```\r\nNo exceptions.\r\n\r\n#### Actual Results\r\n```\r\nno n_init\r\nn_init=5\r\nTraceback (most recent call last):\r\n File \"test_gm.py\", line 17, in \r\n assert_array_equal(c1,c2)\r\n File \"/home/scott/.local/lib/python2.7/site-packages/numpy/testing/_private/utils.py\", line 872, in assert_array_equal\r\n verbose=verbose, header='Arrays are not equal')\r\n File \"/home/scott/.local/lib/python2.7/site-packages/numpy/testing/_private/utils.py\", line 796, in assert_array_compare\r\n raise AssertionError(msg)\r\nAssertionError: \r\nArrays are not equal\r\n\r\n(mismatch 88.6%)\r\n x: array([4, 0, 1, 1, 1, 3, 3, 4, 4, 2, 0, 0, 1, 2, 0, 2, 0, 1, 3, 1, 1, 3,\r\n 2, 1, 0, 2, 1, 0, 2, 0, 3, 1, 2, 3, 3, 1, 0, 2, 2, 0, 3, 0, 2, 0,\r\n 4, 2, 3, 0, 4, 2, 4, 1, 0, 2, 2, 1, 3, 2, 1, 4, 0, 2, 2, 1, 1, 2,...\r\n y: array([4, 1, 0, 2, 2, 1, 1, 4, 4, 0, 4, 1, 0, 3, 1, 0, 2, 2, 1, 2, 0, 0,\r\n 1, 0, 4, 1, 0, 4, 0, 1, 1, 2, 3, 1, 4, 0, 1, 4, 4, 4, 0, 1, 0, 2,\r\n 4, 1, 1, 2, 4, 3, 4, 0, 2, 3, 2, 3, 0, 0, 2, 3, 3, 3, 3, 0, 3, 2,...\r\n```\r\n\r\n#### Versions\r\n```\r\nSystem:\r\n python: 2.7.15rc1 (default, Nov 12 2018, 14:31:15) [GCC 7.3.0]\r\n machine: Linux-4.15.0-43-generic-x86_64-with-Ubuntu-18.04-bionic\r\nexecutable: /usr/bin/python\r\n\r\nBLAS:\r\n macros: HAVE_CBLAS=None, NO_ATLAS_INFO=-1\r\ncblas_libs: cblas\r\n lib_dirs: /usr/lib/x86_64-linux-gnu\r\n\r\nPython deps:\r\n Cython: 0.28.5\r\n scipy: 1.2.0\r\nsetuptools: 39.0.1\r\n pip: 19.0.1\r\n numpy: 1.16.0\r\n pandas: 0.23.1\r\n sklearn: 0.20.2\r\n```", "metadata": {"created_at": "2019-02-12T14:32:37Z", "dataset": "swebench_lite", "fail_to_pass_count": 2, "hints_preview": "Indeed the code in fit_predict and the one in predict are not exactly consistent. This should be fixed but we would need to check the math to choose the correct variant, add a test and remove the other one.\nI don't think the math is wrong or inconsistent. I think it's a matter o", "pass_to_pass_count": 54, "version": "0.21"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-13142", "timeout_s": 1800, "verify": ["python -m pytest -q sklearn/mixture/tests/test_bayesian_mixture.py::test_bayesian_mixture_fit_predict_n_init", "python -m pytest -q sklearn/mixture/tests/test_gaussian_mixture.py::test_gaussian_mixture_fit_predict_n_init"]} -{"base_commit": "29345aecf6e8d53ccb3577a3762bb0c263f7558d", "instance_id": "django__django-14382", "instruction": "django-admin startapp with trailing slash in directory name results in error\nDescription\n\t\nBash tab-completion appends trailing slashes to directory names. django-admin startapp name directory/ results in the error:\nCommandError: '' is not a valid app directory. Please make sure the directory is a valid identifier.\nThe error is caused by \u200bline 77 of django/core/management/templates.py by calling basename() on the path with no consideration for a trailing slash:\nself.validate_name(os.path.basename(target), 'directory')\nRemoving potential trailing slashes would solve the problem:\nself.validate_name(os.path.basename(target.rstrip(os.sep)), 'directory')", "metadata": {"created_at": "2021-05-11T10:40:42Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "OK, yes, this seems a case we could handle. I didn't look into exactly why but it works for startproject: $ django-admin startproject ticket32734 testing/ Thanks for the report. Do you fancy making a PR?\nI didn't look into exactly why but it works for startproject This is the rel", "pass_to_pass_count": 188, "version": "4.0"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-14382", "timeout_s": 1800, "verify": ["python -m pytest -q admin_scripts/tests.py::StartApp::test_trailing_slash_in_target_app_directory_name"]} -{"base_commit": "35b03788b0607c1f8d2b64e4fa9e1669b0907ea4", "instance_id": "django__django-13321", "instruction": "Decoding an invalid session data crashes.\nDescription\n\t \n\t\t(last modified by Matt Hegarty)\n\t \nHi\nI recently upgraded my staging server to 3.1. I think that there was an old session which was still active.\nOn browsing to any URL, I get the crash below. It looks similar to \u200bthis issue.\nI cannot login at all with Chrome - each attempt to access the site results in a crash. Login with Firefox works fine.\nThis is only happening on my Staging site, which is running Gunicorn behind nginx proxy.\nInternal Server Error: /overview/\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 215, in _get_session\nreturn self._session_cache\nAttributeError: 'SessionStore' object has no attribute '_session_cache'\nDuring handling of the above exception, another exception occurred:\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 118, in decode\nreturn signing.loads(session_data, salt=self.key_salt, serializer=self.serializer)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 135, in loads\nbase64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age).encode()\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 201, in unsign\nresult = super().unsign(value)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 184, in unsign\nraise BadSignature('Signature \"%s\" does not match' % sig)\ndjango.core.signing.BadSignature: Signature \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\" does not match\nDuring handling of the above exception, another exception occurred:\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py\", line 47, in inner\nresponse = get_response(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py\", line 179, in _get_response\nresponse = wrapped_callback(request, *callback_args, **callback_kwargs)\nFile \"/usr/local/lib/python3.8/site-packages/django/views/generic/base.py\", line 73, in view\nreturn self.dispatch(request, *args, **kwargs)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/mixins.py\", line 50, in dispatch\nif not request.user.is_authenticated:\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 240, in inner\nself._setup()\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 376, in _setup\nself._wrapped = self._setupfunc()\nFile \"/usr/local/lib/python3.8/site-packages/django_otp/middleware.py\", line 38, in _verify_user\nuser.otp_device = None\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 270, in __setattr__\nself._setup()\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 376, in _setup\nself._wrapped = self._setupfunc()\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/middleware.py\", line 23, in \nrequest.user = SimpleLazyObject(lambda: get_user(request))\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/middleware.py\", line 11, in get_user\nrequest._cached_user = auth.get_user(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/__init__.py\", line 174, in get_user\nuser_id = _get_user_session_key(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/__init__.py\", line 58, in _get_user_session_key\nreturn get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 65, in __getitem__\nreturn self._session[key]\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 220, in _get_session\nself._session_cache = self.load()\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/db.py\", line 44, in load\nreturn self.decode(s.session_data) if s else {}\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 122, in decode\nreturn self._legacy_decode(session_data)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 126, in _legacy_decode\nencoded_data = base64.b64decode(session_data.encode('ascii'))\nFile \"/usr/local/lib/python3.8/base64.py\", line 87, in b64decode\nreturn binascii.a2b_base64(s)\nbinascii.Error: Incorrect padding", "metadata": {"created_at": "2020-08-18T10:43:52Z", "dataset": "swebench_lite", "fail_to_pass_count": 355, "hints_preview": "I tried to run clearsessions, but that didn't help. The only workaround was to delete all rows in the django_session table.\nThanks for this report, however I cannot reproduce this issue. Can you provide a sample project? Support for user sessions created by older versions of Djan", "pass_to_pass_count": 0, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-13321", "timeout_s": 1800, "verify": ["python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_save_doesnt_clear_data", "python -m pytest -q -k 'Falsey values (Such as an empty string) are rejected.'", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_session_key_is_read_only", "python -m pytest -q -k 'Strings shorter than 8 characters are rejected.'", "python -m pytest -q -k 'Strings of length 8 and up are accepted and stored.'", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_unpickling_exception", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CookieSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_create_and_save", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_load_overlong_key", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_non_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CacheSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_empty_session_saved", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_flush_empty_without_session_cookie_doesnt_set_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_httponly_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_no_httponly_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_samesite_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_secure_session_cookie", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_delete_on_end", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_delete_on_end_with_custom_domain_and_path", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_save_on_500", "python -m pytest -q sessions_tests/tests.py::SessionMiddlewareTests::test_session_update_error_redirect", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_clear", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_configuration_check", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_delete", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_flush", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_invalid_key_backslash", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_invalid_key_forwardslash", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_items", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_keys", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_save", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_store", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_update", "python -m pytest -q sessions_tests/tests.py::FileSessionPathLibTests::test_values", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_configuration_check", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_invalid_key_backslash", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_invalid_key_forwardslash", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::FileSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_get_decoded", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q -k 'Session repr should be the session key.'", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_sessionmanager_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_extra_session_field", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_get_decoded", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_sessionmanager_save", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CustomDatabaseSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_exists_searches_cache_first", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_items", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_load_overlong_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_non_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_save", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_store", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_update", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionTests::test_values", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_clear", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_delete", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_exists_searches_cache_first", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_flush", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_items", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_keys", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_load_overlong_key", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_non_default_cache", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_save", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_store", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_update", "python -m pytest -q sessions_tests/tests.py::CacheDBSessionWithTimeZoneTests::test_values", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_actual_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_clear", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_clearsessions_command", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_datetime", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_reset", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_seconds", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_custom_expiry_timedelta", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_cycle", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_cycle_with_no_session_cache", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_decode_failure_logged_to_security", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_decode_legacy", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_default_expiry", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_default_hashing_algorith_legacy_decode", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_delete", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_flush", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_get_empty", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_get_expire_at_browser_close", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_has_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_invalid_key", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_items", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_keys", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_new_session", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop_default", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop_default_named_argument", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_pop_no_default_keyerror_raised", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_save_doesnt_clear_data", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_get_decoded", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_key_is_read_only", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_load_does_not_create_record", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_session_save_does_not_resurrect_session_logged_out_in_other_context", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_sessionmanager_save", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_setdefault", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_store", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_update", "python -m pytest -q sessions_tests/tests.py::DatabaseSessionWithTimeZoneTests::test_values"]} -{"base_commit": "3bc4240d979812bd11365ede04c028ea13fdc8c6", "instance_id": "django__django-12983", "instruction": "Make django.utils.text.slugify() strip dashes and underscores\nDescription\n\t \n\t\t(last modified by Elinaldo do Nascimento Monteiro)\n\t \nBug generation slug\nExample:\nfrom django.utils import text\ntext.slugify(\"___This is a test ---\")\noutput: ___this-is-a-test-\nImprovement after correction\nfrom django.utils import text\ntext.slugify(\"___This is a test ---\")\noutput: this-is-a-test\n\u200bPR", "metadata": {"created_at": "2020-05-26T22:02:40Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "The current version of the patch converts all underscores to dashes which (as discussed on the PR) isn't an obviously desired change. A discussion is needed to see if there's consensus about that change.", "pass_to_pass_count": 15, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-12983", "timeout_s": 1800, "verify": ["python -m pytest -q utils_tests/test_text.py::TestUtilsText::test_slugify"]} -{"base_commit": "3ea1ec84cc610f7a9f4f6b354e264565254923ff", "instance_id": "sphinx-doc__sphinx-8474", "instruction": "v3.3 upgrade started generating \"WARNING: no number is assigned for table\" warnings\nWe've updated to Sphinx 3.3 in our documentation, and suddenly the following warning started popping up in our builds when we build either `singlehtml` or `latex`.:\r\n\r\n`WARNING: no number is assigned for table:`\r\n\r\nI looked through the changelog but it didn't seem like there was anything related to `numref` that was changed, but perhaps I missed something? Could anyone point me to a change in the numref logic so I can figure out where these warnings are coming from?", "metadata": {"created_at": "2020-11-22T16:24:25Z", "dataset": "swebench_lite", "fail_to_pass_count": 4, "hints_preview": "I digged into this a little bit more and it seems like the `id` of the table isn't properly making it into `env.toc_fignumbers`. If I set `:name: mylabel`, regardless the I see something like this in `env.toc_fignumbers`\r\n\r\n```\r\n 'pagename': {'table': {'id3': (1,)},\r\n```\r\n\r\nSo it", "pass_to_pass_count": 436, "version": "3.4"}, "repo": "sphinx-doc/sphinx", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sphinx-doc__sphinx-8474", "timeout_s": 1800, "verify": ["python -m pytest -q tests/test_build_html.py::test_numfig_without_numbered_toctree_warn", "python -m pytest -q tests/test_build_html.py::test_numfig_with_numbered_toctree_warn", "python -m pytest -q tests/test_build_html.py::test_numfig_with_prefix_warn", "python -m pytest -q tests/test_build_html.py::test_numfig_with_secnum_depth_warn"]} -{"base_commit": "e8c22f6eac7314be8d92590bfff92ced79ee03e2", "instance_id": "sympy__sympy-24213", "instruction": "collect_factor_and_dimension does not detect equivalent dimensions in addition\nCode to reproduce:\r\n```python\r\nfrom sympy.physics import units\r\nfrom sympy.physics.units.systems.si import SI\r\n\r\nv1 = units.Quantity('v1')\r\nSI.set_quantity_dimension(v1, units.velocity)\r\nSI.set_quantity_scale_factor(v1, 2 * units.meter / units.second)\r\n\r\na1 = units.Quantity('a1')\r\nSI.set_quantity_dimension(a1, units.acceleration)\r\nSI.set_quantity_scale_factor(a1, -9.8 * units.meter / units.second**2)\r\n\r\nt1 = units.Quantity('t1')\r\nSI.set_quantity_dimension(t1, units.time)\r\nSI.set_quantity_scale_factor(t1, 5 * units.second)\r\n\r\nexpr1 = a1*t1 + v1\r\nSI._collect_factor_and_dimension(expr1)\r\n```\r\nResults in:\r\n```\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"C:\\Python\\Python310\\lib\\site-packages\\sympy\\physics\\units\\unitsystem.py\", line 179, in _collect_factor_and_dimension\r\n raise ValueError(\r\nValueError: Dimension of \"v1\" is Dimension(velocity), but it should be Dimension(acceleration*time)\r\n```", "metadata": {"created_at": "2022-11-03T14:00:09Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 31, "version": "1.12"}, "repo": "sympy/sympy", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::sympy__sympy-24213", "timeout_s": 1800, "verify": ["python -m pytest -q -k test_issue_24211"]} -{"base_commit": "36bc47069ce071e80c8129500de3b8664d2058a7", "instance_id": "django__django-13315", "instruction": "limit_choices_to on a ForeignKey can render duplicate options in formfield\nDescription\n\t\nIf you pass a Q object as limit_choices_to on a ForeignKey field involving a join, you may end up with duplicate options in your form.\nSee regressiontest in patch for a clear view on the problem.", "metadata": {"created_at": "2020-08-17T04:24:39Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "hints_preview": "Replying to SmileyChris: I've updated the patch to resolve the conflicts I've had since you flagged this one as \"Ready for checkin\". No real change.\nupdate resolving conflict\nIs there something I can do to get this checked in? I re-read the \u200bTriage docs. As far as I can see \"A de", "pass_to_pass_count": 146, "version": "3.2"}, "repo": "django/django", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::django__django-13315", "timeout_s": 1800, "verify": ["python -m pytest -q model_forms/tests.py::LimitChoicesToTests::test_limit_choices_to_no_duplicates"]} -{"base_commit": "02dc9ed680e7f53f1b0d410dcdd37341c7958eb1", "instance_id": "scikit-learn__scikit-learn-12471", "instruction": "OneHotEncoder ignore unknown error when categories are strings \n#### Description\r\n\r\nThis bug is very specific, but it happens when you set OneHotEncoder to ignore unknown entries.\r\nand your labels are strings. The memory of the arrays is not handled safely and it can lead to a ValueError\r\n\r\nBasically, when you call the transform method it will sets all the unknown strings on your array to OneHotEncoder.categories_[i][0] which is the first category alphabetically sorted given for fit\r\nIf this OneHotEncoder.categories_[i][0] is a long string, and the array that you want to transform has small strings, then it is impossible to fit the whole OneHotEncoder.categories_[i][0] into the entries of the array we want to transform. So OneHotEncoder.categories_[i][0] is truncated and this raise the ValueError.\r\n\r\n\r\n\r\n#### Steps/Code to Reproduce\r\n```\r\n\r\nimport numpy as np\r\nfrom sklearn.preprocessing import OneHotEncoder\r\n\r\n\r\n# It needs to be numpy arrays, the error does not appear \r\n# is you have lists of lists because it gets treated like an array of objects.\r\ntrain = np.array([ '22','333','4444','11111111' ]).reshape((-1,1))\r\ntest = np.array([ '55555', '22' ]).reshape((-1,1))\r\n\r\nohe = OneHotEncoder(dtype=bool,handle_unknown='ignore')\r\n\r\nohe.fit( train )\r\nenc_test = ohe.transform( test )\r\n\r\n```\r\n\r\n\r\n#### Expected Results\r\nHere we should get an sparse matrix 2x4 false everywhere except at (1,1) the '22' that is known\r\n\r\n#### Actual Results\r\n\r\n> ValueError: y contains previously unseen labels: ['111111']\r\n\r\n\r\n#### Versions\r\nSystem:\r\n python: 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609]\r\n machine: Linux-4.4.0-138-generic-x86_64-with-Ubuntu-16.04-xenial\r\nexecutable: /usr/bin/python\r\n\r\nBLAS:\r\n macros: HAVE_CBLAS=None\r\ncblas_libs: openblas, openblas\r\n lib_dirs: /usr/lib\r\n\r\nPython deps:\r\n Cython: 0.25.2\r\n scipy: 0.18.1\r\nsetuptools: 36.7.0\r\n pip: 9.0.1\r\n numpy: 1.15.2\r\n pandas: 0.19.1\r\n sklearn: 0.21.dev0\r\n\r\n\r\n\r\n#### Comments\r\n\r\nI already implemented a fix for this issue, where I check the size of the elements in the array before, and I cast them into objects if necessary.", "metadata": {"created_at": "2018-10-27T10:43:48Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 53, "version": "0.21"}, "repo": "scikit-learn/scikit-learn", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::scikit-learn__scikit-learn-12471", "timeout_s": 1800, "verify": ["python -m pytest -q sklearn/preprocessing/tests/test_encoders.py::test_one_hot_encoder_handle_unknown_strings"]} -{"base_commit": "56bf819c2f4eaf8b36bd8c42c06bb59d5a3bfc0f", "instance_id": "pytest-dev__pytest-7220", "instruction": "Wrong path to test file when directory changed in fixture\nFiles are shown as relative to new directory when working directory is changed in a fixture. This makes it impossible to jump to the error as the editor is unaware of the directory change. The displayed directory should stay relative to the original directory.\r\n\r\ntest_path_error.py:\r\n```python\r\nimport os\r\nimport errno\r\nimport shutil\r\n\r\nimport pytest\r\n\r\n\r\n@pytest.fixture\r\ndef private_dir(): # or (monkeypatch)\r\n out_dir = 'ddd'\r\n\r\n try:\r\n shutil.rmtree(out_dir)\r\n except OSError as ex:\r\n if ex.errno != errno.ENOENT:\r\n raise\r\n os.mkdir(out_dir)\r\n\r\n old_dir = os.getcwd()\r\n os.chdir(out_dir)\r\n yield out_dir\r\n os.chdir(old_dir)\r\n\r\n # Same issue if using:\r\n # monkeypatch.chdir(out_dir)\r\n\r\n\r\ndef test_show_wrong_path(private_dir):\r\n assert False\r\n```\r\n\r\n```diff\r\n+ Expected: test_path_error.py:29: AssertionError\r\n- Displayed: ../test_path_error.py:29: AssertionError\r\n```\r\n\r\nThe full output is:\r\n```\r\n-*- mode: compilation; default-directory: \"~/src/pytest_path_error/\" -*-\r\nCompilation started at Fri Jan 10 00:05:52\r\n\r\nnox\r\nnox > Running session test\r\nnox > Creating virtual environment (virtualenv) using python3.7 in .nox/test\r\nnox > pip install pytest>=5.3\r\nnox > pip freeze\r\nattrs==19.3.0\r\nimportlib-metadata==1.3.0\r\nmore-itertools==8.0.2\r\npackaging==20.0\r\npluggy==0.13.1\r\npy==1.8.1\r\npyparsing==2.4.6\r\npytest==5.3.2\r\nsix==1.13.0\r\nwcwidth==0.1.8\r\nzipp==0.6.0\r\nnox > pytest \r\n================================= test session starts =================================\r\nplatform linux -- Python 3.7.5, pytest-5.3.2, py-1.8.1, pluggy-0.13.1\r\nrootdir: /home/lhn/src/pytest_path_error\r\ncollected 1 item \r\n\r\ntest_path_error.py F [100%]\r\n\r\n====================================== FAILURES =======================================\r\n________________________________ test_show_wrong_path _________________________________\r\n\r\nprivate_dir = 'ddd'\r\n\r\n def test_show_wrong_path(private_dir):\r\n> assert False\r\nE assert False\r\n\r\n../test_path_error.py:29: AssertionError\r\n================================== 1 failed in 0.03s ==================================\r\nnox > Command pytest failed with exit code 1\r\nnox > Session test failed.\r\n\r\nCompilation exited abnormally with code 1 at Fri Jan 10 00:06:01\r\n```\r\n\r\nnoxfile.py:\r\n```python\r\nimport nox\r\n\r\n@nox.session(python='3.7')\r\ndef test(session):\r\n session.install('pytest>=5.3')\r\n session.run('pip', 'freeze')\r\n session.run('pytest')\r\n```", "metadata": {"created_at": "2020-05-16T14:57:17Z", "dataset": "swebench_lite", "fail_to_pass_count": 1, "pass_to_pass_count": 11, "version": "5.4"}, "repo": "pytest-dev/pytest", "sandbox_image": null, "setup": [], "source": "swebench_lite", "task_id": "swebench_lite::pytest-dev__pytest-7220", "timeout_s": 1800, "verify": ["python -m pytest -q testing/test_nodes.py::test_failure_with_changed_cwd"]} diff --git a/scripts/swe/build_mini_subset.py b/scripts/swe/build_mini_subset.py deleted file mode 100644 index d4c40a81b..000000000 --- a/scripts/swe/build_mini_subset.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python3 -"""Build a deterministic 20-task SWE mini subset. - -Examples --------- -Build from the Hugging Face datasets-server API (default): - - uv run python scripts/swe/build_mini_subset.py - -Build from a local JSONL dump: - - uv run python scripts/swe/build_mini_subset.py \ - --input-jsonl /path/to/swebench_lite_rows.jsonl -""" - -from __future__ import annotations - -import argparse -import json -from pathlib import Path -import sys -from typing import Any -from urllib.error import HTTPError, URLError -from urllib.parse import urlencode -from urllib.request import urlopen - -REPO_ROOT = Path(__file__).resolve().parents[2] -ENVS_DIR = REPO_ROOT / "envs" -if str(ENVS_DIR) not in sys.path: - sys.path.insert(0, str(ENVS_DIR)) - -from mini_swe_env.task_loader_swebench_lite import ( # noqa: E402 - adapt_swebench_lite_rows, - deterministic_train_eval_split, - load_task_file, - read_jsonl_rows, - write_tasks_jsonl, -) - -DEFAULT_DATASET = "princeton-nlp/SWE-bench_Lite" -DEFAULT_SPLIT = "test" -DEFAULT_TRAIN_OUTPUT = REPO_ROOT / "examples/mini_swe_env/tasks/mini_swe_train.jsonl" -DEFAULT_EVAL_OUTPUT = REPO_ROOT / "examples/mini_swe_env/tasks/mini_swe_eval.jsonl" -DATASET_SERVER_ROWS_URL = "https://datasets-server.huggingface.co/rows" - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - "--input-jsonl", - type=Path, - default=None, - help="Optional local SWE-bench Lite JSONL rows. If omitted, fetches from datasets-server.", - ) - parser.add_argument( - "--dataset", - default=DEFAULT_DATASET, - help=f"HF dataset id when fetching remotely (default: {DEFAULT_DATASET}).", - ) - parser.add_argument( - "--split", - default=DEFAULT_SPLIT, - help=f"Dataset split when fetching remotely (default: {DEFAULT_SPLIT}).", - ) - parser.add_argument( - "--max-rows", - type=int, - default=None, - help="Optional cap for remotely fetched rows (useful for smoke runs).", - ) - parser.add_argument("--seed", type=int, default=17) - parser.add_argument("--subset-size", type=int, default=20) - parser.add_argument("--train-size", type=int, default=16) - parser.add_argument("--strict", action="store_true") - parser.add_argument( - "--train-output", - type=Path, - default=DEFAULT_TRAIN_OUTPUT, - ) - parser.add_argument( - "--eval-output", - type=Path, - default=DEFAULT_EVAL_OUTPUT, - ) - return parser.parse_args() - - -def main() -> None: - args = parse_args() - - if args.input_jsonl: - rows = read_jsonl_rows(args.input_jsonl) - source_desc = str(args.input_jsonl) - else: - rows = fetch_rows_from_dataset_server( - dataset=args.dataset, - split=args.split, - max_rows=args.max_rows, - ) - source_desc = f"hf://{args.dataset}[{args.split}]" - - tasks, skipped = adapt_swebench_lite_rows(rows, strict=args.strict) - train_tasks, eval_tasks = deterministic_train_eval_split( - tasks, - subset_size=args.subset_size, - train_size=args.train_size, - seed=args.seed, - ) - - write_tasks_jsonl(args.train_output, train_tasks) - write_tasks_jsonl(args.eval_output, eval_tasks) - - # Final safety check: output must parse as valid SWETask rows. - _ = load_task_file(args.train_output) - _ = load_task_file(args.eval_output) - - print( - "Built mini SWE subset:", - json.dumps( - { - "source": source_desc, - "seed": args.seed, - "rows_seen": len(rows), - "valid_tasks": len(tasks), - "skipped_rows": len(skipped), - "train_tasks": len(train_tasks), - "eval_tasks": len(eval_tasks), - "train_output": str(args.train_output), - "eval_output": str(args.eval_output), - }, - indent=2, - sort_keys=True, - ), - ) - - if skipped: - print("Sample skipped rows (up to 5):") - for skip in skipped[:5]: - print( - f" line={skip.row_index} instance_id={skip.instance_id or '-'} " - f"reason={skip.reason}" - ) - - -def fetch_rows_from_dataset_server( - *, dataset: str, split: str, max_rows: int | None -) -> list[dict[str, Any]]: - rows: list[dict[str, Any]] = [] - offset = 0 - page_size = 100 - - while True: - if max_rows is not None: - remaining = max_rows - len(rows) - if remaining <= 0: - break - page_length = min(page_size, remaining) - else: - page_length = page_size - - payload = _fetch_rows_page( - dataset=dataset, - split=split, - offset=offset, - length=page_length, - ) - page_rows = payload.get("rows", []) - if not page_rows: - break - - for item in page_rows: - row = item.get("row") if isinstance(item, dict) else None - if isinstance(row, dict): - rows.append(row) - - offset += len(page_rows) - - total = payload.get("num_rows_total") - if isinstance(total, int) and offset >= total: - break - - if len(page_rows) < page_length: - break - - if not rows: - raise RuntimeError(f"No rows fetched for dataset={dataset!r} split={split!r}.") - return rows - - -def _fetch_rows_page( - *, dataset: str, split: str, offset: int, length: int -) -> dict[str, Any]: - query = urlencode( - { - "dataset": dataset, - "config": "default", - "split": split, - "offset": offset, - "length": length, - } - ) - url = f"{DATASET_SERVER_ROWS_URL}?{query}" - try: - with urlopen(url, timeout=60) as response: - data = json.loads(response.read().decode("utf-8")) - except HTTPError as exc: - raise RuntimeError( - f"datasets-server request failed ({exc.code}) for {url}: {exc.reason}" - ) from exc - except URLError as exc: - raise RuntimeError( - f"datasets-server request failed for {url}: {exc.reason}" - ) from exc - - if not isinstance(data, dict): - raise RuntimeError( - f"datasets-server response must be a JSON object, got {type(data).__name__}" - ) - return data - - -if __name__ == "__main__": - main() diff --git a/tests/envs/test_swe_task_loader.py b/tests/envs/test_swe_task_loader.py deleted file mode 100644 index 6ac474b46..000000000 --- a/tests/envs/test_swe_task_loader.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. - -"""Unit tests for SWE-bench Lite -> SWETask adaptation.""" - -from __future__ import annotations - -import json -import random - -import pytest -from mini_swe_env.task_loader_swebench_lite import ( - adapt_swebench_lite_row, - coerce_swe_task, - deterministic_train_eval_split, - SOURCE_NAME, - SWEBenchLiteAdapterError, - SWETask, - SWETaskValidationError, -) - - -def test_row_to_swetask_conversion_uses_fail_to_pass_for_verify() -> None: - row = { - "repo": "psf/requests", - "instance_id": "requests__requests-12345", - "base_commit": "a" * 40, - "problem_statement": "Fix redirect edge case in Session.send.", - "FAIL_TO_PASS": json.dumps( - [ - "tests/test_sessions.py::test_redirect_loop", - "tests/test_sessions.py::test_strip_auth", - ] - ), - "PASS_TO_PASS": json.dumps(["tests/test_api.py::test_get"]), - "created_at": "2024-01-01", - "version": "1.0", - } - - task = adapt_swebench_lite_row(row) - - assert task.source == SOURCE_NAME - assert task.task_id == f"{SOURCE_NAME}::{row['instance_id']}" - assert task.instance_id == row["instance_id"] - assert task.repo == row["repo"] - assert task.base_commit == row["base_commit"] - assert task.setup == [] - assert task.verify == [ - "python -m pytest -q tests/test_sessions.py::test_redirect_loop", - "python -m pytest -q tests/test_sessions.py::test_strip_auth", - ] - assert task.metadata["dataset"] == SOURCE_NAME - assert task.metadata["fail_to_pass_count"] == 2 - assert task.metadata["pass_to_pass_count"] == 1 - - -def test_deterministic_split_is_stable_for_seed_and_input_order() -> None: - tasks = [ - SWETask( - task_id=f"{SOURCE_NAME}::id-{idx:03d}", - source=SOURCE_NAME, - instance_id=f"id-{idx:03d}", - repo="example/repo", - base_commit="b" * 40, - instruction=f"Task {idx}", - setup=[], - verify=["python -m pytest -q tests/test_sample.py::test_ok"], - ) - for idx in range(40) - ] - - train_a, eval_a = deterministic_train_eval_split( - tasks, - subset_size=20, - train_size=16, - seed=99, - ) - - shuffled = tasks[:] - random.Random(123).shuffle(shuffled) - train_b, eval_b = deterministic_train_eval_split( - shuffled, - subset_size=20, - train_size=16, - seed=99, - ) - - assert [task.task_id for task in train_a] == [task.task_id for task in train_b] - assert [task.task_id for task in eval_a] == [task.task_id for task in eval_b] - - -def test_row_conversion_handles_unittest_style_names() -> None: - row = { - "repo": "django/django", - "instance_id": "django__django-12915", - "base_commit": "d" * 40, - "problem_statement": "Add async handler method.", - "FAIL_TO_PASS": json.dumps( - [ - "test_get_async_response (staticfiles_tests.test_handlers.TestASGIStaticFilesHandler)" - ] - ), - } - - task = adapt_swebench_lite_row(row) - - assert task.verify == [ - "python -m pytest -q " - "staticfiles_tests/test_handlers.py::TestASGIStaticFilesHandler::test_get_async_response" - ] - - -def test_schema_validation_failures_are_explicit() -> None: - with pytest.raises( - SWETaskValidationError, match="verify must contain at least one" - ): - coerce_swe_task( - { - "task_id": "x", - "source": SOURCE_NAME, - "instance_id": "x", - "repo": "org/repo", - "base_commit": "c" * 40, - "instruction": "Do something", - "setup": [], - "verify": [], - } - ) - - -def test_adapter_error_is_explicit_when_required_fields_missing() -> None: - with pytest.raises(SWEBenchLiteAdapterError, match="missing required field"): - adapt_swebench_lite_row({"instance_id": "missing_repo"}) From afb9bd92e7e0f66e0b14525ebae1d765c5d4cb09 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 14:50:45 +0530 Subject: [PATCH 19/79] feat: SWE-Gym task model and loader --- envs/mini_swe_env/__init__.py | 44 +- envs/mini_swe_env/grading.py | 253 ++++++++++ envs/mini_swe_env/harness.py | 504 ++++++++++++++++++++ envs/mini_swe_env/models.py | 98 +++- envs/mini_swe_env/server/swe_environment.py | 251 +++++++--- envs/mini_swe_env/task_loader_swegym.py | 243 ++++++++++ 6 files changed, 1323 insertions(+), 70 deletions(-) create mode 100644 envs/mini_swe_env/grading.py create mode 100644 envs/mini_swe_env/harness.py create mode 100644 envs/mini_swe_env/task_loader_swegym.py diff --git a/envs/mini_swe_env/__init__.py b/envs/mini_swe_env/__init__.py index e1ef9deae..f66ccc002 100644 --- a/envs/mini_swe_env/__init__.py +++ b/envs/mini_swe_env/__init__.py @@ -1,8 +1,14 @@ -"""Mini SWE environment — task models + environment. +"""Mini SWE environment — SWE-Gym task loader + grading + environment. Exports: Task models: - SWETask, validate_swe_task, coerce_swe_task, SWETaskValidationError + SWEGymTask, SWETask, validate_swe_task, coerce_swe_task, ... + + Task loader (SWE-Gym): + load_swegym_tasks, validate_swegym_task, get_instance_image, ... + + Grading: + grade_from_log, grade_from_test_output, make_eval_script, GradeResult Environment + client: SWEEnvironment, MiniSWEEnv, SWERolloutResult, ... @@ -10,6 +16,7 @@ from .models import ( SWECommandResult, + SWEGymTask, SWERolloutResult, SWEState, SWETask, @@ -18,15 +25,46 @@ validate_swe_task, ) +from .task_loader_swegym import ( + SWEGymLoadError, + get_instance_image, + load_swegym_tasks, + load_swegym_tasks_from_dicts, + swegym_task_to_swe_task, + validate_swegym_task, +) + +from .grading import ( + GradeResult, + GradingError, + grade_from_log, + grade_from_test_output, + make_eval_script, +) + from .client import MiniSWEEnv __all__ = [ # Task models + "SWEGymTask", "SWETask", "SWETaskValidationError", "coerce_swe_task", "validate_swe_task", - # Models + # Task loader + "SWEGymLoadError", + "get_instance_image", + "load_swegym_tasks", + "load_swegym_tasks_from_dicts", + "swegym_task_to_swe_task", + "validate_swegym_task", + # Grading + "GradeResult", + "GradingError", + "grade_from_log", + "grade_from_test_output", + "make_eval_script", + # Server models "MiniSWEEnv", "SWECommandResult", "SWERolloutResult", diff --git a/envs/mini_swe_env/grading.py b/envs/mini_swe_env/grading.py new file mode 100644 index 000000000..641e456ac --- /dev/null +++ b/envs/mini_swe_env/grading.py @@ -0,0 +1,253 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Binary grading for SWE-Gym tasks via ``swebench.harness.grading``. + +Wraps the upstream ``get_eval_report()`` function to produce a binary +reward: **1.0** if the instance is fully resolved (all FAIL_TO_PASS tests +now pass, all PASS_TO_PASS tests still pass), **0.0** otherwise. + +Two entry points: + +1. :func:`grade_from_log` — grade from a test log file (typical + in-sandbox flow: run eval script → parse log → grade). + +2. :func:`grade_from_test_output` — grade from raw test stdout/stderr + (useful when the test runner output is captured directly). + +Both require a :class:`SWEGymTask` for the ground-truth test lists. +""" + +from __future__ import annotations + +import logging +import os +import tempfile +from typing import Any + +from .models import SWEGymTask + +_log = logging.getLogger(__name__) + + +class GradingError(RuntimeError): + """Raised when grading fails due to infrastructure issues.""" + + +# ── Public API ───────────────────────────────────────────────────────────── + + +def grade_from_log( + task: SWEGymTask, + log_path: str, + *, + model_name: str = "agent", + model_patch: str | None = None, +) -> GradeResult: + """Grade a submission using a test log file. + + Args: + task: The SWE-Gym task (provides FAIL_TO_PASS, PASS_TO_PASS, etc.). + log_path: Path to the evaluation log file (output of the + ``swebench`` eval script). + model_name: Identifier for the model producing the patch. + model_patch: The git diff of the agent's changes. If ``None``, + a placeholder is used (``get_eval_report`` only checks + whether the patch field is non-None, the content is not + re-applied). + + Returns: + A :class:`GradeResult` with binary ``reward`` and metadata. + """ + if not os.path.isfile(log_path): + raise GradingError(f"Log file not found: {log_path}") + + test_spec = _task_to_test_spec(task) + prediction = { + "instance_id": task.instance_id, + "model_name_or_path": model_name, + "model_patch": model_patch if model_patch is not None else "placeholder", + } + + from swebench.harness.grading import get_eval_report + + report = get_eval_report( + test_spec=test_spec, + prediction=prediction, + test_log_path=log_path, + include_tests_status=True, + ) + + return _report_to_grade_result(report, task.instance_id) + + +def grade_from_test_output( + task: SWEGymTask, + test_output: str, + *, + model_name: str = "agent", + model_patch: str | None = None, +) -> GradeResult: + """Grade a submission from raw test output text. + + Writes the output to a temporary file and delegates to + :func:`grade_from_log`. + + Args: + task: The SWE-Gym task. + test_output: Combined stdout/stderr from running the eval script. + model_name: Identifier for the model. + model_patch: The git diff (or placeholder). + + Returns: + A :class:`GradeResult`. + """ + with tempfile.NamedTemporaryFile( + mode="w", suffix=".log", delete=False, prefix="swe_grade_" + ) as f: + f.write(test_output) + tmp_path = f.name + + try: + return grade_from_log( + task, + tmp_path, + model_name=model_name, + model_patch=model_patch, + ) + finally: + try: + os.unlink(tmp_path) + except OSError: + pass + + +def make_eval_script(task: SWEGymTask) -> str: + """Generate the swebench evaluation shell script for a task. + + This script is intended to be deployed into the sandbox and run + after the agent applies its patch. The script: + + 1. Applies the ``test_patch`` (new/changed tests from the task). + 2. Runs the test command for the repo/version. + 3. Reverts the ``test_patch`` so the working tree is clean. + + The output is bounded by ``>>>>> Start Test Output`` / ``>>>>> End + Test Output`` markers that ``swebench.harness.grading.get_logs_eval`` + expects. + + Returns: + Shell script content as a string. + """ + test_spec = _task_to_test_spec(task) + + return test_spec.eval_script + + +# ── GradeResult ──────────────────────────────────────────────────────────── + + +class GradeResult: + """Result of grading a SWE-Gym submission. + + Attributes: + reward: Binary reward (1.0 if resolved, 0.0 otherwise). + resolved: Whether the instance was fully resolved. + patch_applied: Whether the agent's patch was successfully applied. + tests_status: Detailed per-test status (if available). + instance_id: The task's instance id. + """ + + __slots__ = ( + "reward", + "resolved", + "patch_applied", + "tests_status", + "instance_id", + ) + + def __init__( + self, + *, + reward: float, + resolved: bool, + patch_applied: bool, + tests_status: dict[str, Any] | None = None, + instance_id: str = "", + ): + self.reward = reward + self.resolved = resolved + self.patch_applied = patch_applied + self.tests_status = tests_status + self.instance_id = instance_id + + def __repr__(self) -> str: + return ( + f"GradeResult(reward={self.reward}, resolved={self.resolved}, " + f"patch_applied={self.patch_applied}, instance_id={self.instance_id!r})" + ) + + +# ── Internal helpers ─────────────────────────────────────────────────────── + + +def _task_to_test_spec(task: SWEGymTask) -> Any: + """Convert a :class:`SWEGymTask` to a ``swebench`` ``TestSpec``.""" + from swebench.harness.test_spec.test_spec import make_test_spec + + # Build the instance dict that make_test_spec expects. + instance: dict[str, Any] = { + "instance_id": task.instance_id, + "repo": task.repo, + "base_commit": task.base_commit, + "version": task.version, + "patch": task.patch, + "test_patch": task.test_patch, + "problem_statement": task.problem_statement, + "hints_text": task.hints_text, + "FAIL_TO_PASS": list(task.FAIL_TO_PASS), + "PASS_TO_PASS": list(task.PASS_TO_PASS), + } + + return make_test_spec(instance) + + +def _report_to_grade_result( + report: dict[str, Any], + instance_id: str, +) -> GradeResult: + """Convert a ``get_eval_report`` output dict to a :class:`GradeResult`.""" + if instance_id not in report: + # Report didn't contain this instance — treat as failure. + _log.warning("Instance %s not in eval report", instance_id) + return GradeResult( + reward=0.0, + resolved=False, + patch_applied=False, + instance_id=instance_id, + ) + + entry = report[instance_id] + resolved = bool(entry.get("resolved", False)) + patch_applied = bool(entry.get("patch_successfully_applied", False)) + tests_status = entry.get("tests_status") + + return GradeResult( + reward=1.0 if resolved else 0.0, + resolved=resolved, + patch_applied=patch_applied, + tests_status=tests_status, + instance_id=instance_id, + ) + + +__all__ = [ + "GradeResult", + "GradingError", + "grade_from_log", + "grade_from_test_output", + "make_eval_script", +] diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py new file mode 100644 index 000000000..bd772d893 --- /dev/null +++ b/envs/mini_swe_env/harness.py @@ -0,0 +1,504 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""SWE harness session and session factory. + +Integrates ``mini_swe_env`` with the ``CLIAgentDriver`` / ``ResourceSession`` +harness infrastructure so that ``build_harness_rollout_func`` works with SWE +tasks out of the box. + +Session lifecycle:: + + factory = SWESessionFactory(agent="pi", config=..., sandbox_backend=..., ...) + session = factory.create(task=swe_task) + + # Training loop uses session.initial_messages(), session.next_request(), etc. + session.wait_for_completion(timeout_s=600) + vr = session.verify(transcript=[]) + print(vr.env_reward) + session.close() + +The factory handles: + 1. Sandbox creation + 2. Repo staging (git clone + checkout to ``base_commit``) + 3. In-sandbox MCP server deployment (``terminal`` tool via stdio) + 4. Setup command execution + 5. Agent bootstrap + launch + 6. Interception gate rollout registration (when mode="interception_gate") + +The session handles: + - ``verify()`` — runs task verify commands, computes reward + - ``initial_messages()`` — instruction prompt + - Interception gate: ``next_request()`` / ``deliver()`` +""" + +from __future__ import annotations + +import asyncio +import json +import logging +import time +import uuid +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Literal + +from openenv.core.harness import Message, ResourceSessionFactory, VerifyResult +from openenv.core.harness.agents import get_agent_spec +from openenv.core.harness.agents.cli_driver import ( + CLIAgentDriver, + CLIAgentSession, +) +from openenv.core.harness.agents.interception_server import InterceptionServer +from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle + +from .models import coerce_swe_task, SWETask, validate_swe_task + + +_log = logging.getLogger(__name__) + +# Sandbox filesystem layout (must match sandbox_mcp_server.py). +HOME = "/home/user" +WORKDIR = "/testbed" +REWARD_FILE = f"{HOME}/logs/verifier/reward.txt" +MCP_CONFIG_PATH = f"{HOME}/.swe_mcp_config.json" +MCP_SERVER_PATH = f"{HOME}/.swe_mcp_server.py" +MCP_PORT = 8765 +VERIFY_TIMEOUT_S = 300 +SETUP_TIMEOUT_S = 600 + +# Source of the in-sandbox MCP server script. +_SANDBOX_MCP_SERVER_SOURCE = Path(__file__).parent / "server" / "sandbox_mcp_server.py" + + +@dataclass +class SWEAgentConfig: + """Minimal config for the CLI agent driver.""" + + base_url: str = "" + api_key: str = "" + model: str = "" + agent_timeout_s: float = 600.0 + sandbox_home: str = HOME + provider: str = "" + thinking: str = "off" + + +@dataclass +class _SWEAgentTask: + """Internal task shape passed to CLIAgentDriver (not SWETask).""" + + instruction: str = "" + setup_shell: str | None = None + upload_files: dict[str, str] = field(default_factory=dict) + metadata: dict[str, Any] = field(default_factory=dict) + + +# ── SWE Session ──────────────────────────────────────────────────────────── + + +class SWESession(CLIAgentSession): + """Per-rollout session with SWE-specific verify and reward logic. + + Extends :class:`CLIAgentSession` with: + - ``verify()`` that runs the task's verify commands and computes reward + - SWE task metadata + """ + + def __init__( + self, + *, + swe_task: SWETask, + verify_timeout_s: int = VERIFY_TIMEOUT_S, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) + self._swe_task = swe_task + self._verify_timeout_s = verify_timeout_s + + @property + def swe_task(self) -> SWETask: + return self._swe_task + + def initial_messages(self) -> list[Message]: + """Return the SWE instruction as the initial prompt.""" + return [{"role": "user", "content": self._swe_task.instruction}] + + def verify( + self, + transcript: list[Message], + final_state: Any | None = None, + ) -> VerifyResult: + """Run verify commands in the sandbox and compute reward. + + Reward = passed_commands / total_commands unless + ``/home/user/logs/verifier/reward.txt`` contains an explicit float. + """ + passed = 0 + verify_details: list[dict[str, Any]] = [] + + for cmd in self._swe_task.verify: + t0 = time.time() + try: + r = self.sandbox.exec(cmd, cwd=WORKDIR, timeout=self._verify_timeout_s) + detail = { + "cmd": cmd, + "exit_code": r.exit_code, + "stdout_tail": (r.stdout or "")[-2000:], + "stderr_tail": (r.stderr or "")[-2000:], + "duration_s": round(time.time() - t0, 3), + } + if r.exit_code == 0: + passed += 1 + except Exception as exc: + detail = { + "cmd": cmd, + "exit_code": -1, + "error": f"{type(exc).__name__}: {exc}", + "duration_s": round(time.time() - t0, 3), + } + verify_details.append(detail) + + # Reward: explicit reward.txt overrides computed ratio. + reward = self._read_reward() + if reward is None and self._swe_task.verify: + reward = passed / len(self._swe_task.verify) + + return VerifyResult( + env_reward=reward, + done=True, + metrics={ + "verify_passed": passed, + "verify_total": len(self._swe_task.verify), + "instance_id": self._swe_task.instance_id, + }, + artifacts={ + "verify_details": verify_details, + "task_id": self._swe_task.task_id, + }, + ) + + def _read_reward(self) -> float | None: + """Read explicit reward override from the sandbox.""" + try: + raw = self.sandbox.read_text(REWARD_FILE) + if raw and raw.strip(): + return float(raw.strip()) + except Exception: + pass + return None + + +# ── Tool-call parsing ────────────────────────────────────────────────────── + + +def parse_terminal_call(text: str) -> dict[str, Any] | None: + """Parse a terminal tool-call from text. + + Handles multiple formats: + - ``{"command": "..."}`` + - ``{"final_answer": "..."}`` + - ``terminal(command="...")`` (Python-style) + + Returns parsed arguments dict or None if not a terminal call. + """ + text = text.strip() + if not text: + return None + + # Try direct JSON parse. + if text.startswith("{"): + try: + data = json.loads(text) + if isinstance(data, dict) and ("command" in data or "final_answer" in data): + return data + except json.JSONDecodeError: + pass + + # Try extracting JSON from markdown code fences. + if "```" in text: + for block in text.split("```"): + block = block.strip() + if block.startswith("json"): + block = block[4:].strip() + if block.startswith("{"): + try: + data = json.loads(block) + if isinstance(data, dict) and ( + "command" in data or "final_answer" in data + ): + return data + except json.JSONDecodeError: + continue + + # Try Python-style: terminal(command="...") or terminal(final_answer="...") + for key in ("command", "final_answer"): + prefix = f"terminal({key}=" + if prefix in text: + idx = text.index(prefix) + len(prefix) + rest = text[idx:] + # Try to extract quoted string + if rest.startswith('"') or rest.startswith("'"): + quote = rest[0] + end = rest.find(quote, 1) + if end > 0: + return {key: rest[1:end]} + + return None + + +# ── SWE Session Factory ─────────────────────────────────────────────────── + + +class SWESessionFactory(ResourceSessionFactory): + """Creates isolated SWE sessions from ``SWETask`` inputs. + + Compatible with :func:`build_harness_rollout_func`. + """ + + def __init__( + self, + *, + agent: str = "pi", + config: SWEAgentConfig, + sandbox_backend: SandboxBackend, + mode: Literal["black_box", "interception_gate"] = "black_box", + install_timeout_s: int = 300, + setup_timeout_s: int = SETUP_TIMEOUT_S, + verify_timeout_s: int = VERIFY_TIMEOUT_S, + interception_server: InterceptionServer | None = None, + interception_base_url: str | None = None, + ) -> None: + if mode not in {"black_box", "interception_gate"}: + raise ValueError(f"Unknown mode: {mode!r}") + if mode == "interception_gate": + if interception_server is None: + raise ValueError( + "interception_gate mode requires an InterceptionServer." + ) + if interception_base_url is None: + raise ValueError( + "interception_gate mode requires interception_base_url." + ) + + self._agent_name = agent + self._config = config + self._backend = sandbox_backend + self._mode = mode + self._verify_timeout_s = verify_timeout_s + self._interception_server = interception_server + self._interception_base_url = interception_base_url + + self._spec = get_agent_spec(agent) + self._driver = CLIAgentDriver( + spec=self._spec, + sandbox_backend=sandbox_backend, + mode=mode, + install_timeout_s=install_timeout_s, + setup_timeout_s=setup_timeout_s, + interception_server=interception_server, + interception_base_url=interception_base_url, + ) + + def create( + self, + task: Any, + seed: int | None = None, + episode_id: str | None = None, + ) -> SWESession: + """Create one SWE session. + + ``task`` can be an ``SWETask``, a dict matching the SWETask schema, + or a JSONL row from SWE-bench Lite. + """ + swe_task = coerce_swe_task(task) if not isinstance(task, SWETask) else task + validate_swe_task(swe_task) + + sandbox_timeout = int(self._config.agent_timeout_s) + 600 + sandbox = self._backend.create( + timeout_s=sandbox_timeout, + metadata={"episode_id": episode_id, "instance_id": swe_task.instance_id} + if episode_id + else {"instance_id": swe_task.instance_id}, + ) + + try: + # 1. Stage repo. + self._stage_repo(sandbox, swe_task) + + # 2. Deploy terminal MCP server (script + config). + self._deploy_terminal_tool(sandbox, swe_task) + + # 3. Run task setup commands. + self._run_setup(sandbox, swe_task) + + # 4. Bootstrap agent (install CLI, write instruction, etc.). + agent_task = self._build_agent_task(swe_task) + self._driver._bootstrap_sandbox(sandbox, agent_task, self._config) + + # 5. Override MCP config to include terminal tool. + self._write_terminal_mcp_config(sandbox) + + except Exception as exc: + _log.error("SWESessionFactory.create: bootstrap failed: %r", exc) + sandbox.kill() + raise + + # 6. Handle interception gate rollout registration. + base_url_override: str | None = None + interception_rollout_id: str | None = None + interception_queue: asyncio.Queue | None = None + + if self._mode == "interception_gate": + assert self._interception_server is not None + assert self._interception_base_url is not None + rollout_id = episode_id or f"rollout_{uuid.uuid4().hex[:8]}" + interception_rollout_id = rollout_id + interception_queue = self._interception_server.register_rollout(rollout_id) + base_url_override = ( + f"{self._interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1" + ) + + # 7. Start agent. + agent_task = self._build_agent_task(swe_task) + agent_bg = self._driver._start_agent( + sandbox, agent_task, self._config, base_url_override=base_url_override + ) + + return SWESession( + swe_task=swe_task, + verify_timeout_s=self._verify_timeout_s, + spec=self._spec, + sandbox=sandbox, + task=agent_task, + config=self._config, + base_url_override=base_url_override, + agent_bg_job=agent_bg, + interception_server=self._interception_server, + interception_rollout_id=interception_rollout_id, + interception_queue=interception_queue, + ) + + # ── Bootstrap helpers ────────────────────────────────────────────────── + + def _stage_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: + """Clone the repo and reset to base_commit.""" + sandbox.exec(f"mkdir -p {WORKDIR}", timeout=10) + clone_url = f"https://github.com/{task.repo}.git" + r = sandbox.exec( + f"git clone --quiet {clone_url} {WORKDIR}", + timeout=SETUP_TIMEOUT_S, + ) + if r.exit_code != 0: + raise RuntimeError( + f"git clone failed (exit {r.exit_code}): {r.stderr[:500]}" + ) + r = sandbox.exec( + f"git checkout --quiet {task.base_commit}", + cwd=WORKDIR, + timeout=60, + ) + if r.exit_code != 0: + raise RuntimeError( + f"git checkout failed (exit {r.exit_code}): {r.stderr[:500]}" + ) + + def _deploy_terminal_tool(self, sandbox: SandboxHandle, task: SWETask) -> None: + """Write the MCP server script + config into the sandbox.""" + mcp_source = _SANDBOX_MCP_SERVER_SOURCE.read_text() + sandbox.write_text(MCP_SERVER_PATH, mcp_source) + + mcp_config = json.dumps( + { + "workspace": WORKDIR, + "verify_commands": list(task.verify), + "timeout_per_command_s": self._verify_timeout_s, + "output_limit": 16_000, + "port": MCP_PORT, + }, + indent=2, + ) + sandbox.write_text(MCP_CONFIG_PATH, mcp_config) + + # Ensure log dirs exist. + sandbox.exec( + f"mkdir -p {HOME}/logs/verifier {HOME}/logs/agent", + timeout=10, + ) + + def _run_setup(self, sandbox: SandboxHandle, task: SWETask) -> None: + """Run task setup commands in the workspace.""" + for cmd in task.setup: + r = sandbox.exec(cmd, cwd=WORKDIR, timeout=SETUP_TIMEOUT_S) + if r.exit_code != 0: + raise RuntimeError( + f"Setup command failed (exit {r.exit_code}): " + f"{cmd[:120]}\nstderr: {(r.stderr or '')[:500]}" + ) + + def _write_terminal_mcp_config(self, sandbox: SandboxHandle) -> None: + """Override the agent's MCP config to include the terminal tool. + + Uses stdio transport: the agent launches sandbox_mcp_server.py as a + subprocess. This is the most compatible transport across agents. + """ + mcp_json = json.dumps( + { + "mcpServers": { + "swe-terminal": { + "command": "python3", + "args": [MCP_SERVER_PATH, "--stdio"], + "env": {"SWE_MCP_CONFIG": MCP_CONFIG_PATH}, + }, + }, + }, + indent=2, + ) + + # Write to the path the agent spec declares. + home = ( + self._config.sandbox_home if hasattr(self._config, "sandbox_home") else HOME + ) + workdir = f"{home}/workdir" + + if ( + self._spec.mcp_config.method == "config_file" + and self._spec.mcp_config.path_template + ): + mcp_path = self._spec.mcp_config.path_template.format( + workdir=workdir, home=home + ) + sandbox.write_text(mcp_path, mcp_json) + + # Also write to well-known global paths for fallback discovery. + for global_path in [ + f"{home}/.mcp.json", + f"{workdir}/.mcp.json", + ]: + try: + sandbox.write_text(global_path, mcp_json) + except Exception: + pass + + def _build_agent_task(self, swe_task: SWETask) -> _SWEAgentTask: + """Convert SWETask into the shape CLIAgentDriver expects.""" + return _SWEAgentTask( + instruction=swe_task.instruction, + setup_shell=None, # We run setup ourselves before bootstrap. + metadata={ + "task_id": swe_task.task_id, + "instance_id": swe_task.instance_id, + "repo": swe_task.repo, + }, + ) + + +__all__ = [ + "SWEAgentConfig", + "SWESession", + "SWESessionFactory", + "parse_terminal_call", +] diff --git a/envs/mini_swe_env/models.py b/envs/mini_swe_env/models.py index 77e027de2..20becb326 100644 --- a/envs/mini_swe_env/models.py +++ b/envs/mini_swe_env/models.py @@ -7,15 +7,17 @@ """Data models for the mini_swe_env. Contains: - - ``SWETask`` — frozen dataclass representing one SWE task (repo, commit, - instruction, etc.). Shared by the environment server, harness, and - client layers. + - ``SWEGymTask`` — frozen dataclass mirroring the SWE-Gym HF dataset + schema (``instance_id``, ``repo``, ``patch``, ``test_patch``, etc.). + - ``SWETask`` — internal task shape shared across environment, harness, + and client layers. ``SWEGymTask.to_swe_task()`` converts between them. - ``SWERolloutResult`` / ``SWECommandResult`` / ``SWEState`` — Pydantic models for the server's MCP tool. """ from __future__ import annotations +import json from dataclasses import asdict, dataclass, field from typing import Any @@ -24,12 +26,90 @@ DEFAULT_TIMEOUT_S = 1800 +SWEGYM_SOURCE = "swegym" + + +# ── SWEGymTask (matches HF dataset schema) ──────────────────────────────── class SWETaskValidationError(ValueError): """Raised when a task fails schema validation.""" +@dataclass(frozen=True) +class SWEGymTask: + """One SWE-Gym task, mirroring the HuggingFace dataset columns. + + Fields correspond 1:1 to the ``SWE-Gym/SWE-Gym`` dataset: + ``instance_id``, ``repo``, ``base_commit``, ``problem_statement``, + ``version``, ``patch`` (ground-truth), ``test_patch``, + ``FAIL_TO_PASS``, ``PASS_TO_PASS``, ``hints_text``, ``created_at``. + + The ``timeout_s`` field is not in the dataset; it is set by the loader. + """ + + instance_id: str + repo: str + base_commit: str + problem_statement: str + version: str + patch: str + test_patch: str + FAIL_TO_PASS: list[str] + PASS_TO_PASS: list[str] + hints_text: str = "" + created_at: str = "" + timeout_s: int = DEFAULT_TIMEOUT_S + + # ── Derived helpers ──────────────────────────────────────────────── + + @property + def instance_image(self) -> str: + """Docker image name for this task (SWE-Gym convention). + + Uses the ``xingyaoww/`` namespace with ``sweb.eval.x86_64.`` prefix. + Doubles underscores are replaced per SWE-bench convention. + """ + sanitised = self.instance_id.lower().replace("__", "_1776_") + return f"xingyaoww/sweb.eval.x86_64.{sanitised}:latest" + + def to_swe_task(self) -> SWETask: + """Convert to the internal ``SWETask`` used by the harness. + + The ``verify`` list is left empty because Phase 3 uses + ``swebench.harness.grading`` for reward, not shell commands. + The ``instruction`` is the ``problem_statement``. + """ + return SWETask( + task_id=f"{SWEGYM_SOURCE}::{self.instance_id}", + source=SWEGYM_SOURCE, + instance_id=self.instance_id, + repo=self.repo, + base_commit=self.base_commit, + instruction=self.problem_statement, + setup=[], + verify=[], # grading via swebench, not shell commands + timeout_s=self.timeout_s, + sandbox_image=self.instance_image, + metadata={ + "version": self.version, + "patch": self.patch, + "test_patch": self.test_patch, + "FAIL_TO_PASS": list(self.FAIL_TO_PASS), + "PASS_TO_PASS": list(self.PASS_TO_PASS), + "hints_text": self.hints_text, + "created_at": self.created_at, + }, + ) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a plain dictionary.""" + return asdict(self) + + +# ── SWETask (internal task shape) ────────────────────────────────────────── + + @dataclass(frozen=True) class SWETask: """One SWE task (repo + commit + instruction + verify commands). @@ -81,12 +161,6 @@ def validate_swe_task(task: SWETask) -> None: if not isinstance(task.verify, list): errors.append("verify must be a list[str]") - elif not task.verify: - errors.append("verify must contain at least one command") - else: - for idx, command in enumerate(task.verify): - if not isinstance(command, str) or not command.strip(): - errors.append(f"verify[{idx}] must be a non-empty string") if not isinstance(task.timeout_s, int) or task.timeout_s <= 0: errors.append("timeout_s must be a positive int") @@ -117,6 +191,9 @@ def coerce_swe_task(value: SWETask | dict[str, Any]) -> SWETask: return task +# ── Pydantic models (server payloads) ───────────────────────────────────── + + class SWECommandResult(BaseModel): """Outcome of one shell command in setup or verify.""" @@ -139,8 +216,9 @@ class SWERolloutResult(BaseModel): instance_id: str = "" sandbox_id: str = "" - # Scalars + # Scalars — binary reward (1.0 resolved, 0.0 not resolved) reward: float | None = None + resolved: bool | None = None agent_exit_code: int | None = None wall_s: float = 0.0 diff --git a/envs/mini_swe_env/server/swe_environment.py b/envs/mini_swe_env/server/swe_environment.py index 989f038f2..9795ee9cc 100644 --- a/envs/mini_swe_env/server/swe_environment.py +++ b/envs/mini_swe_env/server/swe_environment.py @@ -4,19 +4,22 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""SWE environment implementation. +"""SWE environment implementation (v2 — SWE-Gym + swebench grading). -Single MCP tool ``run_swe_rollout`` with the ``SWETask`` shape: +Single MCP tool ``run_swe_rollout`` with the ``SWEGymTask`` shape: - - ``instance_id`` — SWE-bench Lite instance identifier - - ``repo`` — GitHub ``org/repo`` to clone - - ``base_commit`` — commit to reset the repo to - - ``instruction`` — problem statement for the agent - - ``setup`` — bash commands run BEFORE the agent - - ``verify`` — bash commands run AFTER the agent + - ``instance_id`` — SWE-Gym instance identifier + - ``repo`` — GitHub ``org/repo`` + - ``base_commit`` — commit hash in per-task Docker image + - ``problem_statement`` — agent instruction (problem text) -Reward = ``passed_verify_commands / total`` unless a verify command writes -a float to ``/home/user/logs/verifier/reward.txt`` (override). +**Reward** is binary via ``swebench.harness.grading``: + - ``1.0`` if resolved (all FAIL_TO_PASS pass, all PASS_TO_PASS still pass) + - ``0.0`` otherwise + +**Per-task Docker images** from SWE-Gym (``xingyaoww/...``): the repo is +pre-cloned at ``/testbed`` with all dependencies installed. No git clone +or dependency install at runtime. The ``terminal`` tool is delivered via an in-sandbox MCP server (:mod:`sandbox_mcp_server`) started before the agent launches. @@ -25,6 +28,7 @@ from __future__ import annotations import json +import logging import time from pathlib import Path from typing import Any, Optional @@ -36,19 +40,46 @@ from openenv.core.env_server.mcp_environment import MCPEnvironment from openenv.core.env_server.types import Action, Observation - from ..models import SWECommandResult, SWERolloutResult, SWEState, SWETask, validate_swe_task + from ..grading import GradeResult, grade_from_log, make_eval_script + from ..models import ( + SWECommandResult, + SWEGymTask, + SWERolloutResult, + SWEState, + SWETask, + validate_swe_task, + ) + from ..task_loader_swegym import ( + get_instance_image, + validate_swegym_task, + ) except ImportError: # pragma: no cover - from models import SWECommandResult, SWERolloutResult, SWEState, SWETask, validate_swe_task # type: ignore + from grading import GradeResult, grade_from_log, make_eval_script # type: ignore + from models import ( # type: ignore + SWECommandResult, + SWEGymTask, + SWERolloutResult, + SWEState, + SWETask, + validate_swe_task, + ) + from openenv.core.env_server.mcp_environment import MCPEnvironment + from openenv.core.env_server.types import Action, Observation + from task_loader_swegym import get_instance_image, validate_swegym_task # type: ignore +_log = logging.getLogger(__name__) # Long timeout for the single MCP tool (sandbox cold-start + agent run + # verify can take 10-30 min for real SWE tasks). _RUN_ROLLOUT_TIMEOUT_S = 2400.0 -# Sandbox filesystem layout. +# Sandbox filesystem layout — SWE-Gym convention. +# Per-task images have the repo pre-cloned at /testbed. +TESTBED = "/testbed" HOME = "/home/user" -WORKDIR = f"{HOME}/workdir" REWARD_FILE = f"{HOME}/logs/verifier/reward.txt" +EVAL_LOG_FILE = f"{HOME}/logs/verifier/eval.log" +EVAL_SCRIPT_PATH = f"{HOME}/swe_eval.sh" FINAL_ANSWER_FILE = f"{HOME}/logs/agent/final_answer.txt" DONE_MARKER = f"{HOME}/logs/agent/.done" MCP_CONFIG_PATH = f"{HOME}/.swe_mcp_config.json" @@ -68,7 +99,13 @@ class SWEEnvironment(MCPEnvironment): - """Per-session SWE environment exposing ``run_swe_rollout`` MCP tool.""" + """Per-session SWE environment exposing ``run_swe_rollout`` MCP tool. + + Updated for SWE-Gym: + - Per-task Docker images (no git clone needed). + - Binary reward via ``swebench.harness.grading``. + - Working directory is ``/testbed``. + """ SUPPORTS_CONCURRENT_SESSIONS = True @@ -85,7 +122,7 @@ def __init__(self) -> None: @mcp.tool def run_swe_rollout( - # Task fields (match SWETask shape). + # Task fields (match SWEGymTask / SWETask shape). instance_id: str = "", repo: str = "", base_commit: str = "", @@ -108,8 +145,8 @@ def run_swe_rollout( """Run one SWE rollout end-to-end. Pass either individual fields (instance_id, repo, ...) or a - complete SWETask as ``task_json``. Returns a JSON-serialized - ``SWERolloutResult``. + complete SWETask/SWEGymTask as ``task_json``. Returns a + JSON-serialized ``SWERolloutResult``. """ return self._run_swe_rollout_impl( instance_id=instance_id, @@ -257,20 +294,21 @@ def _run_swe_rollout_impl( sandbox = None session = None try: - backend = self._create_backend( - sandbox_backend, sandbox_image or task.sandbox_image - ) + # Use per-task image if available (SWE-Gym convention). + image = sandbox_image or task.sandbox_image + backend = self._create_backend(sandbox_backend, image) sandbox = backend.create( timeout_s=int(agent_timeout_s) + 600, ) result.sandbox_id = sandbox.sandbox_id - # ── Stage repo ──────────────────────────────────────────── - self._stage_repo(sandbox, task) + # ── Stage repo (only if not using a per-task image) ─────── + if not image: + self._stage_repo(sandbox, task) # ── Run setup commands ──────────────────────────────────── for cmd in task.setup: - cr = self._exec_command(sandbox, cmd, cwd=WORKDIR) + cr = self._exec_command(sandbox, cmd, cwd=TESTBED) result.setup_results.append(cr) if cr.exit_code != 0: result.error = f"Setup failed (exit {cr.exit_code}): {cmd[:120]}" @@ -294,8 +332,6 @@ def _run_swe_rollout_impl( ) rollout_task = self._build_agent_task(task) - # Use the already-created sandbox rather than creating a new one. - # We build the session manually using the driver's helpers. from openenv.core.harness.agents.cli_driver import CLIAgentDriver driver = CLIAgentDriver( @@ -303,7 +339,6 @@ def _run_swe_rollout_impl( sandbox_backend=backend, mode="black_box", ) - # Agent install + file upload (instruction, mcp config, etc.) driver._bootstrap_sandbox(sandbox, rollout_task, config) agent_bg = driver._start_agent(sandbox, rollout_task, config) @@ -325,22 +360,16 @@ def _run_swe_rollout_impl( except TimeoutError as exc: result.error = f"Agent timeout: {exc}" - # ── Verify ──────────────────────────────────────────────── - verify_passed = 0 - for cmd in task.verify: - cr = self._exec_command(sandbox, cmd, cwd=WORKDIR) - result.verify_results.append(cr) - if cr.exit_code == 0: - verify_passed += 1 - - # ── Reward ──────────────────────────────────────────────── - override = self._read_reward(sandbox) - if override is not None: - result.reward = override - elif task.verify: - result.reward = verify_passed / len(task.verify) + # ── Grade submission ────────────────────────────────────── + grade_result = self._grade_submission(sandbox, task) + if grade_result is not None: + result.reward = grade_result.reward + result.resolved = grade_result.resolved else: - result.reward = None + # Fallback: legacy verify commands + result.reward, result.resolved = self._legacy_verify( + sandbox, task + ) # ── Collect artifacts ────────────────────────────────────── result.files, result.files_extra = self._collect_files(sandbox) @@ -397,6 +426,12 @@ def _resolve_task( if task_json: try: raw = json.loads(task_json) + # Try SWEGymTask first — it has richer metadata. + if "problem_statement" in raw and "patch" in raw: + gym_task = SWEGymTask(**raw) + validate_swegym_task(gym_task) + return gym_task.to_swe_task() + # Fall back to SWETask. task = SWETask(**raw) validate_swe_task(task) return task @@ -412,6 +447,9 @@ def _resolve_task( if not instance_id: instance_id = f"manual::{repo}::{base_commit[:12]}" + # Derive per-task image. + image = get_instance_image(instance_id) if instance_id else None + try: task = SWETask( task_id=task_id or f"swegym::{instance_id}", @@ -423,12 +461,115 @@ def _resolve_task( setup=setup, verify=verify, timeout_s=timeout_s, + sandbox_image=image, ) validate_swe_task(task) return task except Exception as exc: return f"Task validation failed: {exc}" + # ── Grading ──────────────────────────────────────────────────────────── + + def _grade_submission( + self, + sandbox: Any, + task: SWETask, + ) -> GradeResult | None: + """Run swebench grading if task metadata contains SWE-Gym fields. + + Returns GradeResult on success, None if task lacks metadata for + swebench grading (falls back to legacy verify). + """ + metadata = task.metadata + if not metadata: + return None + + # Need SWE-Gym fields for proper grading. + required_keys = {"patch", "test_patch", "FAIL_TO_PASS", "version"} + if not required_keys.issubset(metadata.keys()): + return None + + try: + gym_task = SWEGymTask( + instance_id=task.instance_id, + repo=task.repo, + base_commit=task.base_commit, + problem_statement=task.instruction, + version=str(metadata["version"]), + patch=str(metadata["patch"]), + test_patch=str(metadata["test_patch"]), + FAIL_TO_PASS=list(metadata["FAIL_TO_PASS"]), + PASS_TO_PASS=list(metadata.get("PASS_TO_PASS", [])), + hints_text=str(metadata.get("hints_text", "")), + created_at=str(metadata.get("created_at", "")), + timeout_s=task.timeout_s, + ) + except Exception as exc: + _log.warning("Could not reconstruct SWEGymTask for grading: %s", exc) + return None + + try: + # Deploy eval script and run it. + eval_script = make_eval_script(gym_task) + sandbox.write_text(EVAL_SCRIPT_PATH, eval_script) + sandbox.exec(f"chmod +x {EVAL_SCRIPT_PATH}", timeout=5) + + r = sandbox.exec( + f"bash {EVAL_SCRIPT_PATH} 2>&1 | tee {EVAL_LOG_FILE}", + cwd=TESTBED, + timeout=VERIFY_TIMEOUT_S, + ) + + # Grade from log file. + # We need the log file on the host, so read it from sandbox. + log_content = sandbox.read_text(EVAL_LOG_FILE) + + import tempfile + + with tempfile.NamedTemporaryFile( + mode="w", suffix=".log", delete=False, prefix="swe_grade_" + ) as f: + f.write(log_content) + tmp_log = f.name + + try: + result = grade_from_log(gym_task, tmp_log) + finally: + import os + + os.unlink(tmp_log) + + # Write reward to sandbox for the answer extension to find. + sandbox.write_text(REWARD_FILE, str(result.reward)) + + return result + + except Exception as exc: + _log.warning("swebench grading failed, falling back: %s", exc) + return None + + def _legacy_verify( + self, + sandbox: Any, + task: SWETask, + ) -> tuple[float | None, bool | None]: + """Legacy verify: run verify commands, compute pass ratio.""" + verify_passed = 0 + for cmd in task.verify: + cr = self._exec_command(sandbox, cmd, cwd=TESTBED) + if cr.exit_code == 0: + verify_passed += 1 + + override = self._read_reward(sandbox) + if override is not None: + return override, override == 1.0 + + if task.verify: + ratio = verify_passed / len(task.verify) + return ratio, ratio == 1.0 + + return None, None + # ── Sandbox helpers ──────────────────────────────────────────────────── def _create_backend(self, backend_name: str, image: str | None) -> Any: @@ -441,13 +582,16 @@ def _create_backend(self, backend_name: str, image: str | None) -> Any: return create_sandbox_backend(backend_name, **kwargs) def _stage_repo(self, sandbox: Any, task: SWETask) -> None: - """Clone the repo and reset to base_commit in the sandbox.""" - sandbox.exec(f"mkdir -p {WORKDIR}", timeout=10) + """Clone the repo and reset to base_commit in the sandbox. + + Only used when there is no per-task Docker image. SWE-Gym + per-task images already have the repo at ``/testbed``. + """ + sandbox.exec(f"mkdir -p {TESTBED}", timeout=10) - # Clone repo clone_url = f"https://github.com/{task.repo}.git" r = sandbox.exec( - f"git clone --quiet {clone_url} {WORKDIR}", + f"git clone --quiet {clone_url} {TESTBED}", timeout=SETUP_TIMEOUT_S, ) if r.exit_code != 0: @@ -455,10 +599,9 @@ def _stage_repo(self, sandbox: Any, task: SWETask) -> None: f"git clone failed (exit {r.exit_code}): {r.stderr[:500]}" ) - # Reset to base commit r = sandbox.exec( f"git checkout --quiet {task.base_commit}", - cwd=WORKDIR, + cwd=TESTBED, timeout=60, ) if r.exit_code != 0: @@ -468,14 +611,12 @@ def _stage_repo(self, sandbox: Any, task: SWETask) -> None: def _deploy_mcp_server(self, sandbox: Any, task: SWETask) -> None: """Write the MCP server script and config into the sandbox, then start it.""" - # Write the MCP server script mcp_source = _SANDBOX_MCP_SERVER_SOURCE.read_text() sandbox.write_text(MCP_SERVER_PATH, mcp_source) - # Write the config mcp_config = json.dumps( { - "workspace": WORKDIR, + "workspace": TESTBED, "verify_commands": list(task.verify), "timeout_per_command_s": VERIFY_TIMEOUT_S, "output_limit": 16_000, @@ -485,19 +626,16 @@ def _deploy_mcp_server(self, sandbox: Any, task: SWETask) -> None: ) sandbox.write_text(MCP_CONFIG_PATH, mcp_config) - # Ensure log dirs exist sandbox.exec( f"mkdir -p {HOME}/logs/verifier {HOME}/logs/agent", timeout=10, ) - # Start the MCP server as a background process sandbox.start_bg( f"python3 {MCP_SERVER_PATH}", envs={"SWE_MCP_CONFIG": MCP_CONFIG_PATH}, ) - # Wait for server to be ready for attempt in range(10): r = sandbox.exec( f"curl -sf http://127.0.0.1:{MCP_PORT}/health 2>/dev/null || echo FAIL", @@ -605,9 +743,8 @@ def _read_reward(self, sandbox: Any) -> float | None: def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: """Collect modified files from the workspace.""" - # Use git diff to find changed files (more relevant than a blind find) listing = sandbox.exec( - f"cd {WORKDIR} && git diff --name-only HEAD 2>/dev/null | head -32", + f"cd {TESTBED} && git diff --name-only HEAD 2>/dev/null | head -32", timeout=10, ) files: dict[str, str] = {} @@ -616,7 +753,7 @@ def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: rel_path = line.strip() if not rel_path: continue - full_path = f"{WORKDIR}/{rel_path}" + full_path = f"{TESTBED}/{rel_path}" try: content = sandbox.read_text(full_path) if len(content) <= 16_000: diff --git a/envs/mini_swe_env/task_loader_swegym.py b/envs/mini_swe_env/task_loader_swegym.py new file mode 100644 index 000000000..3593022e9 --- /dev/null +++ b/envs/mini_swe_env/task_loader_swegym.py @@ -0,0 +1,243 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""SWE-Gym task loader. + +Loads tasks from the HuggingFace ``SWE-Gym/SWE-Gym`` and +``SWE-Gym/SWE-Gym-Lite`` datasets and converts them to +:class:`SWEGymTask` instances. + +Usage:: + + from mini_swe_env.task_loader_swegym import load_swegym_tasks + + # Full dataset (2,438 tasks) + tasks = load_swegym_tasks("full") + + # Lite subset (230 tasks) + tasks = load_swegym_tasks("lite") +""" + +from __future__ import annotations + +import json +import logging +from typing import Any, Sequence + +from .models import DEFAULT_TIMEOUT_S, SWEGymTask, SWETask + +_log = logging.getLogger(__name__) + +# HuggingFace dataset identifiers. +_HF_DATASETS: dict[str, str] = { + "full": "SWE-Gym/SWE-Gym", + "lite": "SWE-Gym/SWE-Gym-Lite", +} + +# All SWE-Gym datasets use the "train" split. +_HF_SPLIT = "train" + + +class SWEGymLoadError(ValueError): + """Raised when a SWE-Gym dataset cannot be loaded or a row is invalid.""" + + +# ── Public API ───────────────────────────────────────────────────────────── + + +def load_swegym_tasks( + variant: str = "lite", + *, + timeout_s: int = DEFAULT_TIMEOUT_S, +) -> list[SWEGymTask]: + """Load SWE-Gym tasks from HuggingFace. + + Args: + variant: ``"lite"`` (230 tasks) or ``"full"`` (2,438 tasks). + timeout_s: Per-task timeout override. + + Returns: + List of validated :class:`SWEGymTask` instances. + + Raises: + SWEGymLoadError: If the variant is unknown or the dataset fails + to load. + """ + dataset_name = _HF_DATASETS.get(variant) + if dataset_name is None: + raise SWEGymLoadError( + f"Unknown variant {variant!r}; choose from {sorted(_HF_DATASETS)}" + ) + + try: + from datasets import load_dataset + except ImportError as exc: + raise SWEGymLoadError( + "The 'datasets' package is required to load SWE-Gym tasks. " + "Install it with: pip install datasets" + ) from exc + + _log.info("Loading SWE-Gym dataset %s (split=%s) ...", dataset_name, _HF_SPLIT) + + try: + ds = load_dataset(dataset_name, split=_HF_SPLIT) + except Exception as exc: + raise SWEGymLoadError( + f"Failed to load HuggingFace dataset {dataset_name!r}: {exc}" + ) from exc + + tasks: list[SWEGymTask] = [] + for idx, row in enumerate(ds): + try: + task = _row_to_swegym_task(row, timeout_s=timeout_s) + validate_swegym_task(task) + tasks.append(task) + except Exception as exc: + _log.warning( + "Skipping row %d (%s): %s", + idx, + row.get("instance_id", "?"), + exc, + ) + + _log.info("Loaded %d SWE-Gym tasks from %s", len(tasks), dataset_name) + return tasks + + +def load_swegym_tasks_from_dicts( + rows: Sequence[dict[str, Any]], + *, + timeout_s: int = DEFAULT_TIMEOUT_S, +) -> list[SWEGymTask]: + """Convert raw dicts (e.g. from JSONL) to :class:`SWEGymTask` instances. + + Args: + rows: Iterable of dicts matching the SWE-Gym HF schema. + timeout_s: Per-task timeout override. + + Returns: + List of validated :class:`SWEGymTask` instances. + """ + tasks: list[SWEGymTask] = [] + for row in rows: + task = _row_to_swegym_task(row, timeout_s=timeout_s) + validate_swegym_task(task) + tasks.append(task) + return tasks + + +def swegym_task_to_swe_task(task: SWEGymTask) -> SWETask: + """Convert a :class:`SWEGymTask` to the internal :class:`SWETask`. + + Convenience wrapper around ``task.to_swe_task()``. + """ + return task.to_swe_task() + + +def validate_swegym_task(task: SWEGymTask) -> None: + """Validate a :class:`SWEGymTask` and raise on errors.""" + errors: list[str] = [] + + for field_name in ("instance_id", "repo", "base_commit", "problem_statement"): + value = getattr(task, field_name) + if not isinstance(value, str) or not value.strip(): + errors.append(f"{field_name} must be a non-empty string") + + if not isinstance(task.version, str): + errors.append("version must be a string") + + if not isinstance(task.patch, str) or not task.patch.strip(): + errors.append("patch (ground truth) must be a non-empty string") + + if not isinstance(task.test_patch, str) or not task.test_patch.strip(): + errors.append("test_patch must be a non-empty string") + + if not isinstance(task.FAIL_TO_PASS, list) or not task.FAIL_TO_PASS: + errors.append("FAIL_TO_PASS must be a non-empty list") + + if not isinstance(task.PASS_TO_PASS, list): + errors.append("PASS_TO_PASS must be a list") + + if not isinstance(task.timeout_s, int) or task.timeout_s <= 0: + errors.append("timeout_s must be a positive int") + + if errors: + raise SWEGymLoadError( + f"Invalid SWEGymTask {task.instance_id!r}: {'; '.join(errors)}" + ) + + +def get_instance_image(instance_id: str) -> str: + """Derive the per-task Docker image name from an instance id. + + SWE-Gym convention: + ``xingyaoww/sweb.eval.x86_64.:latest`` + where ``__`` in the instance id is replaced with ``_1776_``. + """ + sanitised = instance_id.lower().replace("__", "_1776_") + return f"xingyaoww/sweb.eval.x86_64.{sanitised}:latest" + + +# ── Internal helpers ─────────────────────────────────────────────────────── + + +def _coerce_string_list(value: Any) -> list[str]: + """Coerce a value to ``list[str]``. + + Handles: ``list``, JSON-encoded string, ``None``. + """ + if value is None: + return [] + if isinstance(value, list): + return [str(item) for item in value if str(item).strip()] + if isinstance(value, str): + stripped = value.strip() + if not stripped: + return [] + try: + parsed = json.loads(stripped) + except json.JSONDecodeError: + return [stripped] + if isinstance(parsed, list): + return [str(item) for item in parsed if str(item).strip()] + return [str(parsed)] + return [str(value)] + + +def _row_to_swegym_task( + row: dict[str, Any], + *, + timeout_s: int = DEFAULT_TIMEOUT_S, +) -> SWEGymTask: + """Convert one HF dataset row to an :class:`SWEGymTask`.""" + instance_id = row.get("instance_id", "") + if not instance_id: + raise SWEGymLoadError("Row missing required field 'instance_id'") + + return SWEGymTask( + instance_id=str(instance_id), + repo=str(row.get("repo", "")), + base_commit=str(row.get("base_commit", "")), + problem_statement=str(row.get("problem_statement", "")), + version=str(row.get("version", "")), + patch=str(row.get("patch", "")), + test_patch=str(row.get("test_patch", "")), + FAIL_TO_PASS=_coerce_string_list(row.get("FAIL_TO_PASS")), + PASS_TO_PASS=_coerce_string_list(row.get("PASS_TO_PASS")), + hints_text=str(row.get("hints_text", "") or ""), + created_at=str(row.get("created_at", "") or ""), + timeout_s=timeout_s, + ) + + +__all__ = [ + "SWEGymLoadError", + "get_instance_image", + "load_swegym_tasks", + "load_swegym_tasks_from_dicts", + "swegym_task_to_swe_task", + "validate_swegym_task", +] From 8d3b79fd0a7d309f57bd9ba5efb43bde81e4c724 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 19:49:43 +0530 Subject: [PATCH 20/79] feat: integrate pi agent harness --- envs/mini_swe_env/extensions/swe-answer.ts | 85 ++++ envs/mini_swe_env/extensions/swe-grade.sh | 72 +++ envs/mini_swe_env/harness.py | 522 ++++++++++++++------ envs/mini_swe_env/server/swe_environment.py | 18 - 4 files changed, 519 insertions(+), 178 deletions(-) create mode 100644 envs/mini_swe_env/extensions/swe-answer.ts create mode 100644 envs/mini_swe_env/extensions/swe-grade.sh diff --git a/envs/mini_swe_env/extensions/swe-answer.ts b/envs/mini_swe_env/extensions/swe-answer.ts new file mode 100644 index 000000000..3a740029c --- /dev/null +++ b/envs/mini_swe_env/extensions/swe-answer.ts @@ -0,0 +1,85 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +// swe-answer.ts — Pi extension that registers the `answer` tool. +// +// During SWE training, this extension is deployed into the sandbox at +// ~/.pi/agent/extensions/swe-answer.ts so Pi auto-discovers it on startup. +// +// The `answer` tool: +// 1. Runs swe-grade.sh (applies test patch, runs eval, reverts patch). +// 2. Parses stdout for the GRADE line (resolved=true/false). +// 3. Returns "Resolved: true/false" to Pi as a feedback signal. +// 4. Pi either stops (done) or continues if not resolved. +// +// NOTE: This extension does NOT write or read reward.txt. The +// authoritative training reward is computed host-side by +// SWESession.verify() using swebench grading. The result returned +// here is only a fast feedback signal for the agent. +// +// Environment variables (set by the harness): +// SWE_GRADE_SCRIPT — path to swe-grade.sh +// SWE_INSTANCE_ID — task instance id (for logging) + +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; +import { Type } from "typebox"; +import { execSync } from "child_process"; + +export default function (pi: ExtensionAPI) { + const GRADE_SCRIPT = process.env.SWE_GRADE_SCRIPT || "/home/user/swe-grade.sh"; + const INSTANCE_ID = process.env.SWE_INSTANCE_ID || "unknown"; + + pi.registerTool({ + name: "answer", + label: "Submit Answer", + description: + "Submit your solution for grading. Runs the test suite against your changes " + + "and returns whether the issue is resolved. Call this when you believe your " + + "fix is complete. Returns the grading result (Resolved: true/false).", + parameters: Type.Object({}), + + async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) { + let gradeOutput = ""; + let error: string | null = null; + + try { + gradeOutput = execSync(`bash ${GRADE_SCRIPT}`, { + encoding: "utf-8", + timeout: 300_000, // 5 minutes + stdio: ["pipe", "pipe", "pipe"], + env: { ...process.env }, + }); + } catch (e: any) { + // Grade script may exit non-zero if tests fail — that's expected. + gradeOutput = (e.stdout || "") + "\n" + (e.stderr || ""); + error = e.message?.slice(0, 500) || "grading failed"; + } + + // Parse the GRADE line from stdout. + const gradeLine = gradeOutput.split("\n").find(l => l.startsWith("GRADE:")) || ""; + const resolved = gradeLine.includes("resolved=true"); + + const summary = resolved + ? `✅ Resolved: true` + : `❌ Resolved: false`; + + const details = [ + `Instance: ${INSTANCE_ID}`, + `Resolved: ${resolved}`, + error ? `Error: ${error}` : null, + `--- Grade Output (last 2000 chars) ---`, + gradeOutput.slice(-2000), + ] + .filter(Boolean) + .join("\n"); + + return { + content: [{ type: "text", text: `${summary}\n\n${details}` }], + details: { resolved, instance_id: INSTANCE_ID }, + }; + }, + }); +} diff --git a/envs/mini_swe_env/extensions/swe-grade.sh b/envs/mini_swe_env/extensions/swe-grade.sh new file mode 100644 index 000000000..2186fe4bf --- /dev/null +++ b/envs/mini_swe_env/extensions/swe-grade.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# swe-grade.sh — Run swebench-style evaluation in the sandbox. +# +# This script is deployed into the sandbox by SWESessionFactory alongside +# the swe-answer.ts Pi extension. When the agent calls the `answer` tool, +# the extension executes this script and parses stdout for the result. +# +# The script: +# 1. Applies the test_patch (new/changed tests for this task). +# 2. Runs the test command for the repo/version. +# 3. Reverts the test_patch so the working tree is clean. +# 4. Prints the eval exit code so the extension can parse it. +# +# NOTE: This script does NOT write reward.txt. The authoritative reward +# is computed host-side by SWESession.verify() using swebench grading. +# The output here is only used by the answer extension as a fast feedback +# signal for the agent ("Resolved: true/false"). +# +# Environment variables (set by the harness): +# SWE_INSTANCE_ID — e.g. "django__django-11099" +# SWE_TESTBED — working directory, e.g. "/testbed" +# SWE_TEST_PATCH — path to the test patch file +# SWE_EVAL_SCRIPT — path to the swebench eval script +# SWE_LOG_FILE — where to write the eval log + +set -uo pipefail + +TESTBED="${SWE_TESTBED:-/testbed}" +TEST_PATCH="${SWE_TEST_PATCH:-/home/user/swe_test.patch}" +EVAL_SCRIPT="${SWE_EVAL_SCRIPT:-/home/user/swe_eval.sh}" +LOG_FILE="${SWE_LOG_FILE:-/home/user/logs/verifier/eval.log}" + +mkdir -p "$(dirname "$LOG_FILE")" + +cd "$TESTBED" || { echo "ERROR: cannot cd to $TESTBED"; exit 1; } + +# --- Step 1: Apply test patch (if present) --- +if [ -f "$TEST_PATCH" ] && [ -s "$TEST_PATCH" ]; then + echo ">>>>> Applying test patch" + git apply --allow-empty "$TEST_PATCH" 2>&1 || true +fi + +# --- Step 2: Run eval script --- +EVAL_EXIT=0 +if [ -f "$EVAL_SCRIPT" ] && [ -s "$EVAL_SCRIPT" ]; then + echo ">>>>> Running eval script" + bash "$EVAL_SCRIPT" > "$LOG_FILE" 2>&1 || EVAL_EXIT=$? +else + echo ">>>>> No eval script found, skipping" + echo "No eval script" > "$LOG_FILE" +fi + +# --- Step 3: Revert test patch --- +if [ -f "$TEST_PATCH" ] && [ -s "$TEST_PATCH" ]; then + echo ">>>>> Reverting test patch" + git apply --allow-empty -R "$TEST_PATCH" 2>&1 || true +fi + +# --- Step 4: Print result for the answer extension to parse --- +if [ "$EVAL_EXIT" -eq 0 ]; then + echo "GRADE: resolved=true eval_exit=0" +else + echo "GRADE: resolved=false eval_exit=$EVAL_EXIT" +fi + +echo ">>>>> Grade complete (eval_exit=$EVAL_EXIT)" diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index bd772d893..eb316e97f 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -4,33 +4,51 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""SWE harness session and session factory. +"""SWE harness session and session factory (v2 — Pi answer extension). Integrates ``mini_swe_env`` with the ``CLIAgentDriver`` / ``ResourceSession`` -harness infrastructure so that ``build_harness_rollout_func`` works with SWE -tasks out of the box. +harness infrastructure. Pi runs in the sandbox with its built-in tools +(bash, edit, write, read, grep, find, ls) plus one extension-registered +tool: ``answer``. Session lifecycle:: factory = SWESessionFactory(agent="pi", config=..., sandbox_backend=..., ...) - session = factory.create(task=swe_task) + session = factory.create(task=swe_gym_task.to_swe_task()) - # Training loop uses session.initial_messages(), session.next_request(), etc. + # interception_gate mode: + request = await session.next_request() + await session.deliver(request, response_dict) + ... + # OR black_box mode: session.wait_for_completion(timeout_s=600) + vr = session.verify(transcript=[]) - print(vr.env_reward) + print(vr.env_reward) # 1.0 or 0.0 (binary) session.close() +**Reward integrity**: The authoritative reward is computed **host-side** +by ``SWESession.verify()``, which extracts the agent's diff from the +sandbox, runs the swebench eval script in the sandbox, downloads the +test log, and grades it on the host via +``swebench.harness.grading.get_eval_report()``. No ``reward.txt`` is +used — the agent cannot influence the training reward. + +The in-sandbox ``answer`` extension gives the agent a fast feedback +signal ("Resolved: true/false") but this is purely informational and +not used for the training reward. + The factory handles: - 1. Sandbox creation - 2. Repo staging (git clone + checkout to ``base_commit``) - 3. In-sandbox MCP server deployment (``terminal`` tool via stdio) - 4. Setup command execution - 5. Agent bootstrap + launch - 6. Interception gate rollout registration (when mode="interception_gate") + 1. Sandbox creation (per-task Docker image, /testbed ready) + 2. Deploy Pi ``answer`` extension (swe-answer.ts + swe-grade.sh) + 3. Deploy eval script (from swebench via ``make_eval_script``) + 4. Deploy test patch file + 5. Run task setup commands + 6. Agent bootstrap + launch + 7. Interception gate rollout registration (when mode="interception_gate") The session handles: - - ``verify()`` — runs task verify commands, computes reward + - ``verify()`` — host-side grading via swebench, returns binary VerifyResult - ``initial_messages()`` — instruction prompt - Interception gate: ``next_request()`` / ``deliver()`` """ @@ -55,23 +73,31 @@ from openenv.core.harness.agents.interception_server import InterceptionServer from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle -from .models import coerce_swe_task, SWETask, validate_swe_task +from .models import SWEGymTask, SWETask, coerce_swe_task, validate_swe_task _log = logging.getLogger(__name__) -# Sandbox filesystem layout (must match sandbox_mcp_server.py). +# ── Sandbox filesystem layout (SWE-Gym convention) ───────────────────────── + HOME = "/home/user" -WORKDIR = "/testbed" -REWARD_FILE = f"{HOME}/logs/verifier/reward.txt" -MCP_CONFIG_PATH = f"{HOME}/.swe_mcp_config.json" -MCP_SERVER_PATH = f"{HOME}/.swe_mcp_server.py" -MCP_PORT = 8765 +TESTBED = "/testbed" +EVAL_LOG_FILE = f"{HOME}/logs/verifier/eval.log" + +# Extension + grading script paths in sandbox. +EXTENSION_DIR = f"{HOME}/.pi/agent/extensions" +EXTENSION_PATH = f"{EXTENSION_DIR}/swe-answer.ts" +GRADE_SCRIPT_PATH = f"{HOME}/swe-grade.sh" +EVAL_SCRIPT_PATH = f"{HOME}/swe_eval.sh" +TEST_PATCH_PATH = f"{HOME}/swe_test.patch" + VERIFY_TIMEOUT_S = 300 SETUP_TIMEOUT_S = 600 -# Source of the in-sandbox MCP server script. -_SANDBOX_MCP_SERVER_SOURCE = Path(__file__).parent / "server" / "sandbox_mcp_server.py" +# Source files for the answer extension and grading script. +_EXTENSIONS_DIR = Path(__file__).parent / "extensions" +_SWE_ANSWER_TS = _EXTENSIONS_DIR / "swe-answer.ts" +_SWE_GRADE_SH = _EXTENSIONS_DIR / "swe-grade.sh" @dataclass @@ -104,8 +130,13 @@ class SWESession(CLIAgentSession): """Per-rollout session with SWE-specific verify and reward logic. Extends :class:`CLIAgentSession` with: - - ``verify()`` that runs the task's verify commands and computes reward - - SWE task metadata + - ``verify()`` that grades host-side via ``swebench.harness.grading`` + (never trusts in-sandbox files for reward). + - Falls back to running verify commands for legacy tasks. + - SWE task metadata. + + **Reward integrity**: The training reward is always computed on the + host. The agent cannot influence it by writing files in the sandbox. """ def __init__( @@ -132,67 +163,215 @@ def verify( transcript: list[Message], final_state: Any | None = None, ) -> VerifyResult: - """Run verify commands in the sandbox and compute reward. + """Compute the training reward host-side. + + For SWE-Gym tasks (metadata has ``test_patch``, ``FAIL_TO_PASS``, + etc.), the flow is: + + 1. Revert any test files the agent may have modified back to + ``base_commit`` (anti-reward-hacking). + 2. Apply the task's ``test_patch`` in the sandbox. + 3. Run the eval script in the sandbox, capturing the log. + 4. Revert the ``test_patch``. + 5. Download the log to the host. + 6. Grade on the host via ``swebench.harness.grading.get_eval_report()``. + + For legacy tasks with shell ``verify`` commands, falls back to + running them and computing pass ratio. - Reward = passed_commands / total_commands unless - ``/home/user/logs/verifier/reward.txt`` contains an explicit float. + If neither path applies, reward defaults to 0.0. """ - passed = 0 - verify_details: list[dict[str, Any]] = [] - - for cmd in self._swe_task.verify: - t0 = time.time() - try: - r = self.sandbox.exec(cmd, cwd=WORKDIR, timeout=self._verify_timeout_s) - detail = { - "cmd": cmd, - "exit_code": r.exit_code, - "stdout_tail": (r.stdout or "")[-2000:], - "stderr_tail": (r.stderr or "")[-2000:], - "duration_s": round(time.time() - t0, 3), - } - if r.exit_code == 0: - passed += 1 - except Exception as exc: - detail = { - "cmd": cmd, - "exit_code": -1, - "error": f"{type(exc).__name__}: {exc}", - "duration_s": round(time.time() - t0, 3), - } - verify_details.append(detail) - - # Reward: explicit reward.txt overrides computed ratio. - reward = self._read_reward() - if reward is None and self._swe_task.verify: - reward = passed / len(self._swe_task.verify) + # 1. Try host-side swebench grading (primary path). + grade_result = self._host_side_grade() + if grade_result is not None: + return VerifyResult( + env_reward=grade_result.reward, + done=True, + metrics={ + "instance_id": self._swe_task.instance_id, + "reward_source": "host_swebench", + "resolved": grade_result.resolved, + "patch_applied": grade_result.patch_applied, + }, + artifacts={ + "task_id": self._swe_task.task_id, + "tests_status": grade_result.tests_status, + }, + ) + + # 2. Fallback: run verify commands (legacy tasks with shell commands). + if self._swe_task.verify: + passed = 0 + verify_details: list[dict[str, Any]] = [] + + for cmd in self._swe_task.verify: + t0 = time.time() + try: + r = self.sandbox.exec( + cmd, cwd=TESTBED, timeout=self._verify_timeout_s + ) + detail = { + "cmd": cmd, + "exit_code": r.exit_code, + "stdout_tail": (r.stdout or "")[-2000:], + "stderr_tail": (r.stderr or "")[-2000:], + "duration_s": round(time.time() - t0, 3), + } + if r.exit_code == 0: + passed += 1 + except Exception as exc: + detail = { + "cmd": cmd, + "exit_code": -1, + "error": f"{type(exc).__name__}: {exc}", + "duration_s": round(time.time() - t0, 3), + } + verify_details.append(detail) + + fallback_reward = passed / len(self._swe_task.verify) + return VerifyResult( + env_reward=fallback_reward, + done=True, + metrics={ + "verify_passed": passed, + "verify_total": len(self._swe_task.verify), + "instance_id": self._swe_task.instance_id, + "reward_source": "verify_commands", + }, + artifacts={ + "verify_details": verify_details, + "task_id": self._swe_task.task_id, + }, + ) + # 3. No grading source available. return VerifyResult( - env_reward=reward, + env_reward=0.0, done=True, metrics={ - "verify_passed": passed, - "verify_total": len(self._swe_task.verify), "instance_id": self._swe_task.instance_id, + "reward_source": "default_no_grading", }, artifacts={ - "verify_details": verify_details, "task_id": self._swe_task.task_id, }, ) - def _read_reward(self) -> float | None: - """Read explicit reward override from the sandbox.""" + def _host_side_grade(self) -> Any: + """Run swebench grading host-side. + + Returns a :class:`GradeResult` or ``None`` if the task lacks + SWE-Gym metadata. + """ + metadata = self._swe_task.metadata + if not metadata: + return None + + required_keys = {"patch", "test_patch", "FAIL_TO_PASS", "version"} + if not required_keys.issubset(metadata.keys()): + return None + try: - raw = self.sandbox.read_text(REWARD_FILE) - if raw and raw.strip(): - return float(raw.strip()) - except Exception: - pass - return None + from .grading import grade_from_test_output + from .models import SWEGymTask + + gym_task = SWEGymTask( + instance_id=self._swe_task.instance_id, + repo=self._swe_task.repo, + base_commit=self._swe_task.base_commit, + problem_statement=self._swe_task.instruction, + version=str(metadata["version"]), + patch=str(metadata["patch"]), + test_patch=str(metadata["test_patch"]), + FAIL_TO_PASS=list(metadata["FAIL_TO_PASS"]), + PASS_TO_PASS=list(metadata.get("PASS_TO_PASS", [])), + hints_text=str(metadata.get("hints_text", "")), + created_at=str(metadata.get("created_at", "")), + timeout_s=self._swe_task.timeout_s, + ) + except Exception as exc: + _log.warning("Could not reconstruct SWEGymTask: %s", exc) + return None + try: + # Step 1: Revert test files to base_commit (anti-reward-hacking). + self._revert_test_files(gym_task) -# ── Tool-call parsing ────────────────────────────────────────────────────── + # Step 2: Apply the known-good test_patch. + self.sandbox.exec( + f"cd {TESTBED} && git apply --allow-empty {TEST_PATCH_PATH}", + timeout=30, + ) + + # Step 3: Run the eval script, capturing output. + r = self.sandbox.exec( + f"bash {EVAL_SCRIPT_PATH} 2>&1", + cwd=TESTBED, + timeout=self._verify_timeout_s, + ) + test_output = (r.stdout or "") + "\n" + (r.stderr or "") + + # Step 4: Revert the test_patch. + self.sandbox.exec( + f"cd {TESTBED} && git apply --allow-empty -R {TEST_PATCH_PATH}", + timeout=30, + ) + + # Step 5: Extract agent's patch for the grading report. + diff_r = self.sandbox.exec( + f"cd {TESTBED} && git diff HEAD", + timeout=30, + ) + model_patch = diff_r.stdout or "" + + # Step 6: Grade on host. + # Prepend patch-applied marker expected by swebench grading. + test_output = f">>>>> Applied Patch (pred)\n{test_output}" + return grade_from_test_output( + gym_task, + test_output, + model_patch=model_patch if model_patch.strip() else None, + ) + + except Exception as exc: + _log.warning("Host-side grading failed: %s", exc) + return None + + def _revert_test_files(self, gym_task: SWEGymTask) -> None: + """Revert any test files the agent may have modified. + + Uses ``git checkout -- `` for each file in + the test_patch, then removes any new test files the agent added. + This prevents reward hacking by weakening test assertions. + """ + test_patch = gym_task.test_patch + if not test_patch: + return + + # Parse file paths from the test patch. + modified_files: list[str] = [] + for line in test_patch.splitlines(): + if line.startswith("+++ b/"): + path = line[6:] + modified_files.append(path) + elif line.startswith("--- a/"): + path = line[6:] + if path != "/dev/null": + modified_files.append(path) + + if not modified_files: + return + + # Revert each test file to base_commit. + base = gym_task.base_commit + for path in set(modified_files): + self.sandbox.exec( + f"cd {TESTBED} && git checkout {base} -- {path} 2>/dev/null || true", + timeout=10, + ) + + +# ── Tool-call parsing (kept for backward compatibility) ──────────────────── def parse_terminal_call(text: str) -> dict[str, Any] | None: @@ -209,7 +388,6 @@ def parse_terminal_call(text: str) -> dict[str, Any] | None: if not text: return None - # Try direct JSON parse. if text.startswith("{"): try: data = json.loads(text) @@ -218,7 +396,6 @@ def parse_terminal_call(text: str) -> dict[str, Any] | None: except json.JSONDecodeError: pass - # Try extracting JSON from markdown code fences. if "```" in text: for block in text.split("```"): block = block.strip() @@ -234,13 +411,11 @@ def parse_terminal_call(text: str) -> dict[str, Any] | None: except json.JSONDecodeError: continue - # Try Python-style: terminal(command="...") or terminal(final_answer="...") for key in ("command", "final_answer"): prefix = f"terminal({key}=" if prefix in text: idx = text.index(prefix) + len(prefix) rest = text[idx:] - # Try to extract quoted string if rest.startswith('"') or rest.startswith("'"): quote = rest[0] end = rest.find(quote, 1) @@ -254,7 +429,12 @@ def parse_terminal_call(text: str) -> dict[str, Any] | None: class SWESessionFactory(ResourceSessionFactory): - """Creates isolated SWE sessions from ``SWETask`` inputs. + """Creates isolated SWE sessions with Pi answer extension. + + Deploys the ``answer`` tool extension + grading script into each + sandbox so Pi gets fast feedback on its submission. The + authoritative training reward is computed host-side by + ``SWESession.verify()``. Compatible with :func:`build_harness_rollout_func`. """ @@ -309,45 +489,44 @@ def create( seed: int | None = None, episode_id: str | None = None, ) -> SWESession: - """Create one SWE session. + """Create one SWE session with answer extension deployed. - ``task`` can be an ``SWETask``, a dict matching the SWETask schema, - or a JSONL row from SWE-bench Lite. + ``task`` can be an ``SWETask``, ``SWEGymTask``, or a dict. """ - swe_task = coerce_swe_task(task) if not isinstance(task, SWETask) else task + if isinstance(task, SWEGymTask): + swe_task = task.to_swe_task() + elif isinstance(task, SWETask): + swe_task = task + else: + swe_task = coerce_swe_task(task) validate_swe_task(swe_task) sandbox_timeout = int(self._config.agent_timeout_s) + 600 sandbox = self._backend.create( timeout_s=sandbox_timeout, - metadata={"episode_id": episode_id, "instance_id": swe_task.instance_id} - if episode_id - else {"instance_id": swe_task.instance_id}, + metadata=( + {"episode_id": episode_id, "instance_id": swe_task.instance_id} + if episode_id + else {"instance_id": swe_task.instance_id} + ), + image=swe_task.sandbox_image, ) try: - # 1. Stage repo. - self._stage_repo(sandbox, swe_task) - - # 2. Deploy terminal MCP server (script + config). - self._deploy_terminal_tool(sandbox, swe_task) + if not swe_task.sandbox_image: + self._stage_repo(sandbox, swe_task) - # 3. Run task setup commands. + self._deploy_answer_extension(sandbox, swe_task) self._run_setup(sandbox, swe_task) - # 4. Bootstrap agent (install CLI, write instruction, etc.). agent_task = self._build_agent_task(swe_task) self._driver._bootstrap_sandbox(sandbox, agent_task, self._config) - # 5. Override MCP config to include terminal tool. - self._write_terminal_mcp_config(sandbox) - except Exception as exc: _log.error("SWESessionFactory.create: bootstrap failed: %r", exc) sandbox.kill() raise - # 6. Handle interception gate rollout registration. base_url_override: str | None = None interception_rollout_id: str | None = None interception_queue: asyncio.Queue | None = None @@ -362,7 +541,6 @@ def create( f"{self._interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1" ) - # 7. Start agent. agent_task = self._build_agent_task(swe_task) agent_bg = self._driver._start_agent( sandbox, agent_task, self._config, base_url_override=base_url_override @@ -386,10 +564,10 @@ def create( def _stage_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: """Clone the repo and reset to base_commit.""" - sandbox.exec(f"mkdir -p {WORKDIR}", timeout=10) + sandbox.exec(f"mkdir -p {TESTBED}", timeout=10) clone_url = f"https://github.com/{task.repo}.git" r = sandbox.exec( - f"git clone --quiet {clone_url} {WORKDIR}", + f"git clone --quiet {clone_url} {TESTBED}", timeout=SETUP_TIMEOUT_S, ) if r.exit_code != 0: @@ -398,7 +576,7 @@ def _stage_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: ) r = sandbox.exec( f"git checkout --quiet {task.base_commit}", - cwd=WORKDIR, + cwd=TESTBED, timeout=60, ) if r.exit_code != 0: @@ -406,88 +584,98 @@ def _stage_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: f"git checkout failed (exit {r.exit_code}): {r.stderr[:500]}" ) - def _deploy_terminal_tool(self, sandbox: SandboxHandle, task: SWETask) -> None: - """Write the MCP server script + config into the sandbox.""" - mcp_source = _SANDBOX_MCP_SERVER_SOURCE.read_text() - sandbox.write_text(MCP_SERVER_PATH, mcp_source) - - mcp_config = json.dumps( - { - "workspace": WORKDIR, - "verify_commands": list(task.verify), - "timeout_per_command_s": self._verify_timeout_s, - "output_limit": 16_000, - "port": MCP_PORT, - }, - indent=2, - ) - sandbox.write_text(MCP_CONFIG_PATH, mcp_config) + def _deploy_answer_extension( + self, sandbox: SandboxHandle, task: SWETask + ) -> None: + """Deploy the Pi answer extension and grading infrastructure. - # Ensure log dirs exist. + Writes into the sandbox: + - swe-answer.ts → ~/.pi/agent/extensions/ (Pi auto-discovers) + - swe-grade.sh → ~/swe-grade.sh + - swe_eval.sh → ~/swe_eval.sh (from swebench, if available) + - swe_test.patch → ~/swe_test.patch (test patch, if available) + """ sandbox.exec( - f"mkdir -p {HOME}/logs/verifier {HOME}/logs/agent", + f"mkdir -p {EXTENSION_DIR} {HOME}/logs/verifier {HOME}/logs/agent", timeout=10, ) + sandbox.write_text(EXTENSION_PATH, _SWE_ANSWER_TS.read_text()) + + sandbox.write_text(GRADE_SCRIPT_PATH, _SWE_GRADE_SH.read_text()) + sandbox.exec(f"chmod +x {GRADE_SCRIPT_PATH}", timeout=5) + + test_patch = task.metadata.get("test_patch", "") + if test_patch: + sandbox.write_text(TEST_PATCH_PATH, test_patch) + + eval_script = self._generate_eval_script(task) + if eval_script: + sandbox.write_text(EVAL_SCRIPT_PATH, eval_script) + sandbox.exec(f"chmod +x {EVAL_SCRIPT_PATH}", timeout=5) + + # Set environment variables for the grading script. + env_file = f"{HOME}/.swe_env" + env_content = "\n".join([ + f"export SWE_INSTANCE_ID={_shell_quote(task.instance_id)}", + f"export SWE_TESTBED={TESTBED}", + f"export SWE_TEST_PATCH={TEST_PATCH_PATH}", + f"export SWE_EVAL_SCRIPT={EVAL_SCRIPT_PATH}", + f"export SWE_LOG_FILE={EVAL_LOG_FILE}", + f"export SWE_GRADE_SCRIPT={GRADE_SCRIPT_PATH}", + ]) + sandbox.write_text(env_file, env_content) + + sandbox.exec( + f'echo "source {env_file}" >> {HOME}/.bashrc', + timeout=5, + ) + + def _generate_eval_script(self, task: SWETask) -> str | None: + """Try to generate a swebench eval script for this task.""" + metadata = task.metadata + required_keys = {"patch", "test_patch", "FAIL_TO_PASS", "version"} + if not metadata or not required_keys.issubset(metadata.keys()): + return None + + try: + from .grading import make_eval_script + from .models import SWEGymTask + + gym_task = SWEGymTask( + instance_id=task.instance_id, + repo=task.repo, + base_commit=task.base_commit, + problem_statement=task.instruction, + version=str(metadata["version"]), + patch=str(metadata["patch"]), + test_patch=str(metadata["test_patch"]), + FAIL_TO_PASS=list(metadata["FAIL_TO_PASS"]), + PASS_TO_PASS=list(metadata.get("PASS_TO_PASS", [])), + hints_text=str(metadata.get("hints_text", "")), + created_at=str(metadata.get("created_at", "")), + timeout_s=task.timeout_s, + ) + return make_eval_script(gym_task) + except Exception as exc: + _log.debug("Could not generate eval script: %s", exc) + return None + def _run_setup(self, sandbox: SandboxHandle, task: SWETask) -> None: """Run task setup commands in the workspace.""" for cmd in task.setup: - r = sandbox.exec(cmd, cwd=WORKDIR, timeout=SETUP_TIMEOUT_S) + r = sandbox.exec(cmd, cwd=TESTBED, timeout=SETUP_TIMEOUT_S) if r.exit_code != 0: raise RuntimeError( f"Setup command failed (exit {r.exit_code}): " f"{cmd[:120]}\nstderr: {(r.stderr or '')[:500]}" ) - def _write_terminal_mcp_config(self, sandbox: SandboxHandle) -> None: - """Override the agent's MCP config to include the terminal tool. - - Uses stdio transport: the agent launches sandbox_mcp_server.py as a - subprocess. This is the most compatible transport across agents. - """ - mcp_json = json.dumps( - { - "mcpServers": { - "swe-terminal": { - "command": "python3", - "args": [MCP_SERVER_PATH, "--stdio"], - "env": {"SWE_MCP_CONFIG": MCP_CONFIG_PATH}, - }, - }, - }, - indent=2, - ) - - # Write to the path the agent spec declares. - home = ( - self._config.sandbox_home if hasattr(self._config, "sandbox_home") else HOME - ) - workdir = f"{home}/workdir" - - if ( - self._spec.mcp_config.method == "config_file" - and self._spec.mcp_config.path_template - ): - mcp_path = self._spec.mcp_config.path_template.format( - workdir=workdir, home=home - ) - sandbox.write_text(mcp_path, mcp_json) - - # Also write to well-known global paths for fallback discovery. - for global_path in [ - f"{home}/.mcp.json", - f"{workdir}/.mcp.json", - ]: - try: - sandbox.write_text(global_path, mcp_json) - except Exception: - pass - def _build_agent_task(self, swe_task: SWETask) -> _SWEAgentTask: """Convert SWETask into the shape CLIAgentDriver expects.""" return _SWEAgentTask( instruction=swe_task.instruction, - setup_shell=None, # We run setup ourselves before bootstrap. + setup_shell=None, metadata={ "task_id": swe_task.task_id, "instance_id": swe_task.instance_id, @@ -496,9 +684,23 @@ def _build_agent_task(self, swe_task: SWETask) -> _SWEAgentTask: ) +def _shell_quote(s: str) -> str: + """Simple shell quoting for env var values.""" + return "'" + s.replace("'", "'\\''") + "'" + + __all__ = [ "SWEAgentConfig", "SWESession", "SWESessionFactory", "parse_terminal_call", + # Filesystem constants (useful for tests). + "EVAL_LOG_FILE", + "EVAL_SCRIPT_PATH", + "EXTENSION_DIR", + "EXTENSION_PATH", + "GRADE_SCRIPT_PATH", + "HOME", + "TEST_PATCH_PATH", + "TESTBED", ] diff --git a/envs/mini_swe_env/server/swe_environment.py b/envs/mini_swe_env/server/swe_environment.py index 9795ee9cc..b7acc0385 100644 --- a/envs/mini_swe_env/server/swe_environment.py +++ b/envs/mini_swe_env/server/swe_environment.py @@ -77,7 +77,6 @@ # Per-task images have the repo pre-cloned at /testbed. TESTBED = "/testbed" HOME = "/home/user" -REWARD_FILE = f"{HOME}/logs/verifier/reward.txt" EVAL_LOG_FILE = f"{HOME}/logs/verifier/eval.log" EVAL_SCRIPT_PATH = f"{HOME}/swe_eval.sh" FINAL_ANSWER_FILE = f"{HOME}/logs/agent/final_answer.txt" @@ -539,9 +538,6 @@ def _grade_submission( os.unlink(tmp_log) - # Write reward to sandbox for the answer extension to find. - sandbox.write_text(REWARD_FILE, str(result.reward)) - return result except Exception as exc: @@ -560,10 +556,6 @@ def _legacy_verify( if cr.exit_code == 0: verify_passed += 1 - override = self._read_reward(sandbox) - if override is not None: - return override, override == 1.0 - if task.verify: ratio = verify_passed / len(task.verify) return ratio, ratio == 1.0 @@ -731,16 +723,6 @@ def _exec_command( duration_s=round(time.time() - t, 3), ) - def _read_reward(self, sandbox: Any) -> float | None: - """Read explicit reward override from the sandbox.""" - raw = self._safe_read(sandbox, REWARD_FILE).strip() - if not raw: - return None - try: - return float(raw) - except ValueError: - return None - def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: """Collect modified files from the workspace.""" listing = sandbox.exec( From a478fa8bf0c19384c8c7bf3eae74d2206d8ca9ae Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 20:35:47 +0530 Subject: [PATCH 21/79] refactor: extract sandbox bootstrap to driver and fix interception races --- envs/coding_agent_env/README.md | 34 ++--- envs/coding_agent_env/client.py | 9 +- envs/coding_agent_env/harness.py | 54 ++------ envs/coding_agent_env/models.py | 11 +- envs/coding_agent_env/pyproject.toml | 4 +- .../server/coding_environment.py | 69 ++++++---- examples/coding_agent_env_simple.py | 26 +--- src/openenv/core/harness/agents/cli_driver.py | 40 ++++-- .../harness/agents/interception_server.py | 101 ++++++++++---- src/openenv/core/harness/agents/opencode.py | 11 +- src/openenv/core/harness/agents/pi.py | 10 +- src/openenv/core/harness/sandbox/_util.py | 12 ++ .../core/harness/sandbox/docker_backend.py | 43 +++--- .../core/harness/sandbox/hf_backend.py | 22 ++-- tests/core/test_cli_agent_driver.py | 114 ++++++++++++++++ tests/core/test_docker_sandbox_backend.py | 4 +- tests/core/test_hf_sandbox_backend.py | 6 +- tests/core/test_interception_server.py | 124 ++++++++++++++++++ tests/envs/test_coding_agent_env.py | 52 ++++++++ 19 files changed, 541 insertions(+), 205 deletions(-) create mode 100644 src/openenv/core/harness/sandbox/_util.py create mode 100644 tests/core/test_interception_server.py diff --git a/envs/coding_agent_env/README.md b/envs/coding_agent_env/README.md index 11fb88188..7825e5c25 100644 --- a/envs/coding_agent_env/README.md +++ b/envs/coding_agent_env/README.md @@ -9,7 +9,7 @@ app_port: 8000 base_path: /web tags: - openenv -short_description: Multi-harness coding-agent env (OpenCode + Pi) in E2B with logprob capture +short_description: Multi-harness coding-agent env (OpenCode + Pi) in E2B --- # Coding Agent Environment for OpenEnv @@ -17,13 +17,13 @@ short_description: Multi-harness coding-agent env (OpenCode + Pi) in E2B with lo `coding_agent_env` runs coding-agent harnesses (currently [OpenCode](https://opencode.ai) and [Pi](https://github.com/badlogic/pi-mono)) inside an isolated [E2B](https://e2b.dev) sandbox against any OpenAI-compatible -LLM endpoint, optionally capturing per-token logprobs for GRPO training. +LLM endpoint with optional trainer-owned interception for RL training. **🚀 Try it live**: [`AdithyaSK/coding-agent-env`](https://huggingface.co/spaces/AdithyaSK/coding-agent-env) The deployed Space exposes: -- **Web UI** at [`/web`](https://adithyask-coding-agent-env.hf.space/web) — pick endpoint, write task, hit Run, watch live phase log + reward + logprobs. +- **Web UI** at [`/web`](https://adithyask-coding-agent-env.hf.space/web) — pick endpoint, write task, hit Run, watch live phase log + reward. - **MCP tool API** at [`/mcp`](https://adithyask-coding-agent-env.hf.space/mcp) — programmatic `run_rollout` calls. - **OpenAPI docs** at [`/docs`](https://adithyask-coding-agent-env.hf.space/docs). - **Health** at [`/health`](https://adithyask-coding-agent-env.hf.space/health). @@ -83,7 +83,6 @@ async def main(): result = RolloutResult.model_validate_json(_extract_text(raw)) print("reward:", result.reward) - print("turns:", len(result.proxy_turns)) print("files:", list(result.files.keys())) print("wall:", result.wall_s, "s") @@ -95,7 +94,6 @@ Expected output (~20s with the prebaked template): ``` reward: 1.0 -turns: 3 files: ['/home/user/workdir/binary_search.py', ...] wall: 19.8 s ``` @@ -134,11 +132,10 @@ factory = CodingAgentSessionFactory( model="gpt-4o-mini", ), sandbox_backend=E2BSandboxBackend(), - mode="transparent_proxy", # captures per-token logprobs + mode="interception_gate", # trainer-owned interception mode ) session = factory.create(task=CodingAgentTask(instruction="...")) session.wait_for_completion() -turns = session.fetch_proxy_trace() # per-turn (tokens, logprobs) session.close() ``` @@ -195,23 +192,23 @@ directly. | `setup` | `list[str]` | `[]` | Bash commands run **before** the agent. | | `verify` | `list[str]` | `[]` | Bash commands run **after** the agent. | | `task_id` | `str` | `""` | Echoed back in result. | -| `mode` | `str` | `"transparent_proxy"` | Or `"black_box"` (no logprobs). | +| `mode` | `str` | `"black_box"` | Or `"interception_gate"` for trainer-owned generation. | | `disable_thinking` | `bool \| None` | `None` (catalog default) | Inject `chat_template_kwargs.enable_thinking=false`. | | `max_tokens_cap` | `int` | `4096` | Per-turn `max_tokens` clamp. | -| `top_logprobs` | `int` | `5` | HF Router cap is 5; OpenAI 0–20; vLLM unbounded. | +| `top_logprobs` | `int` | `5` | Reserved for trainer-owned interception workflows. | | `agent_timeout_s` | `float` | `600.0` | Hard wall budget for the selected harness. | | `template` | `str` | `""` | E2B template name; `"coding-agent-rl"` skips ~2 min of install per rollout. | Returns `RolloutResult` JSON with: `reward`, `setup_results[]`, -`verify_results[]`, `proxy_turns[]`, `files{}`, `agent_log_tail`, +`verify_results[]`, `files{}`, `agent_log_tail`, `proxy_log_tail`, `wall_s`, `agent_exit_code`, `sandbox_id`, `error`. ## Two Operating Modes | Mode | What it does | Best for | |---|---|---| -| **`transparent_proxy`** (default) | In-sandbox proxy at `localhost:7000` forwards harness LLM calls to `base_url`, injects `logprobs=true`, captures per-turn `(messages, completion_tokens, logprobs)` to `proxy_trace.jsonl`. | GRPO / RL training, observability, top-k distillation. | -| **`black_box`** | No proxy. The selected harness talks straight to `base_url`. | Smoke tests, eval, SFT data collection. | +| **`black_box`** (default) | The selected harness talks directly to `base_url`. | Smoke tests, eval, SFT data collection. | +| **`interception_gate`** | Agent calls are routed through trainer-host interception endpoints. Trainer owns forward pass + trajectory capture. | RL training with trainer-owned generation. | ## Environment Variables @@ -230,21 +227,17 @@ sibling `.env` file; on HF Spaces, set them as **Space secrets**. | **OpenAI endpoint** | | | | `OPENAI_API_KEY` | required for `endpoint="openai"` | Standard OpenAI key. | | `OPENAI_BASE_URL` | no | Defaults to `https://api.openai.com/v1`. | -| `OPENAI_MODEL` | no | Defaults to `gpt-4o-mini` (gpt-5.x and o-series refuse logprobs). | +| `OPENAI_MODEL` | no | Defaults to `gpt-4o-mini`. | | **HF Router endpoint** | | | | `HF_ROUTER_API_KEY` | required for `endpoint="hf_router"` | HF user token. | | `HF_ROUTER_BASE_URL` | no | Defaults to `https://router.huggingface.co/v1`. | | `HF_ROUTER_MODEL` | no | Defaults to `Qwen/Qwen3-4B-Instruct-2507:nscale`. | -Pick `provider:` suffixes that actually return logprobs: -**Together / Nscale / Scaleway / SambaNova / Cerebras**. Avoid Novita / -Hyperbolic / Featherless (silent drop) and Groq (HTTP 400). ## Pre-baked E2B Template The first rollout in a fresh E2B sandbox spends ~2 min installing -harness tooling and the proxy's Python deps. Build a one-time template that -ships those pre-installed: +harness tooling. Build a one-time template that ships those pre-installed: ```bash .venv/bin/python envs/coding_agent_env/sandbox/build_template.py @@ -290,7 +283,8 @@ src/openenv/core/harness/sandbox/ ├── base.py # SandboxBackend / SandboxHandle protocols ├── e2b_backend.py # E2B implementation ├── docker_backend.py # local Docker backend -└── interception.py # in-sandbox FastAPI proxy (logprob capture) +├── hf_backend.py # HF sandbox backend +└── _util.py # shared sandbox shell utilities ``` ## References @@ -299,4 +293,4 @@ src/openenv/core/harness/sandbox/ - [OpenCode CLI](https://opencode.ai/docs/cli/) - [Pi](https://github.com/badlogic/pi-mono) - [E2B Python SDK](https://e2b.dev/docs) -- [HF Inference Providers logprob matrix](../../../DOCS/HF/hf_inference_providers_logprobs.md) + diff --git a/envs/coding_agent_env/client.py b/envs/coding_agent_env/client.py index c1e0f6f92..492060a25 100644 --- a/envs/coding_agent_env/client.py +++ b/envs/coding_agent_env/client.py @@ -25,7 +25,7 @@ verify=["python /home/user/test.py"], task_id="binary_search_v1", ) - print(result.reward, len(result.proxy_turns)) + print(result.reward) """ from __future__ import annotations @@ -95,15 +95,14 @@ def run_rollout( requests. Needed for Qwen3.5 vLLM; harmless on Instruct variants; rejected by OpenAI direct. max_tokens_cap: Clamp on per-turn ``max_tokens``. - top_logprobs: Top-k logprobs requested upstream. HF Router caps - at 5; OpenAI accepts up to 20; vLLM is unbounded. + top_logprobs: Reserved for trainer-owned interception workflows. agent_timeout_s: Hard wall-clock budget for one agent run. template: E2B template name (e.g. ``"coding-agent-rl"``). Empty string uses the default (slow) base image. Returns: - A :class:`RolloutResult` with reward, per-turn logprobs, file - outputs, setup/verify results, and diagnostic tails. + A :class:`RolloutResult` with reward, file outputs, + setup/verify results, and diagnostic tails. """ raw = self.call_tool( "run_rollout", diff --git a/envs/coding_agent_env/harness.py b/envs/coding_agent_env/harness.py index 295b07ac3..2355260f5 100644 --- a/envs/coding_agent_env/harness.py +++ b/envs/coding_agent_env/harness.py @@ -24,12 +24,7 @@ from .opencode_runtime import ( agent_log_path, build_env_vars, - build_install_cmd, - build_opencode_json, build_run_cmd, - instruction_path, - opencode_config_path, - system_prompt_path, ) from .task import CodingAgentTask @@ -87,10 +82,7 @@ def __init__( raise ValueError(f"Unknown mode: {mode!r}") self._config = config self._backend = sandbox_backend - self._mode = mode self._verifier = verifier - self._install_timeout_s = install_timeout_s - self._setup_timeout_s = setup_timeout_s self._driver = CLIAgentDriver( spec=OPENCODE_SPEC, sandbox_backend=sandbox_backend, @@ -111,6 +103,16 @@ def create( _log = logging.getLogger(__name__) oc_task = CodingAgentTask.coerce(task) + setup_parts: list[str] = [] + if self._config.extra_setup_shell: + setup_parts.append(self._config.extra_setup_shell) + if oc_task.setup_shell: + setup_parts.append(oc_task.setup_shell) + if setup_parts: + oc_task = oc_task.model_copy( + update={"setup_shell": "set -e\n" + "\n".join(setup_parts)} + ) + sandbox_timeout = int(self._config.agent_timeout_s) + 300 sandbox = self._backend.create( timeout_s=sandbox_timeout, @@ -132,41 +134,7 @@ def create( return session def _bootstrap_sandbox(self, sandbox: SandboxHandle, task: CodingAgentTask) -> None: - self._driver._wait_for_sandbox_ready(sandbox) - if not self._driver._agent_already_installed(sandbox): - self._driver._exec_with_retry( - sandbox, - build_install_cmd(self._config), - timeout=self._install_timeout_s, - attempts=3, - backoff_s=3.0, - label="opencode install", - ) - sandbox.write_text( - opencode_config_path(self._config), build_opencode_json(self._config) - ) - sandbox.write_text(instruction_path(self._config), task.instruction) - if self._config.system_prompt: - sandbox.write_text( - system_prompt_path(self._config), self._config.system_prompt - ) - for remote_path, content in task.upload_files.items(): - sandbox.write_text(remote_path, content) - if self._config.extra_setup_shell: - self._driver._exec_with_retry( - sandbox, - self._config.extra_setup_shell, - timeout=self._setup_timeout_s, - attempts=2, - backoff_s=2.0, - label="extra_setup_shell", - ) - if task.setup_shell: - r = sandbox.exec(task.setup_shell, timeout=self._setup_timeout_s) - if r.exit_code != 0: - raise RuntimeError( - f"task.setup_shell failed ({r.exit_code}): {r.stderr}" - ) + self._driver.bootstrap_sandbox(sandbox, task, self._config) __all__ = [ diff --git a/envs/coding_agent_env/models.py b/envs/coding_agent_env/models.py index 821b1bd57..2111d84d5 100644 --- a/envs/coding_agent_env/models.py +++ b/envs/coding_agent_env/models.py @@ -21,7 +21,7 @@ class RolloutTurn(BaseModel): - """One intercepted LLM turn captured by the in-sandbox proxy (Mode B).""" + """One intercepted LLM turn shape (trainer-owned in interception_gate mode).""" turn: int finish_reason: str | None = None @@ -45,11 +45,7 @@ class CommandResult(BaseModel): class RolloutResult(BaseModel): - """Full payload returned from one ``run_rollout`` invocation. - - The trainer (or any client) decodes this from the MCP tool result JSON - and feeds ``proxy_turns`` + ``reward`` into GRPO. - """ + """Full payload returned from one ``run_rollout`` invocation.""" # Identifiers task_id: str = "" @@ -65,7 +61,8 @@ class RolloutResult(BaseModel): setup_results: list[CommandResult] = Field(default_factory=list) verify_results: list[CommandResult] = Field(default_factory=list) - # Per-turn LLM trajectory (empty in black_box mode) + # Per-turn LLM trajectory placeholder. Capture is trainer-owned in + # interception_gate mode; environment currently leaves this empty. proxy_turns: list[RolloutTurn] = Field(default_factory=list) # Filesystem the agent produced (path -> contents, truncated) diff --git a/envs/coding_agent_env/pyproject.toml b/envs/coding_agent_env/pyproject.toml index 276d3e0be..d935a0bf5 100644 --- a/envs/coding_agent_env/pyproject.toml +++ b/envs/coding_agent_env/pyproject.toml @@ -11,7 +11,7 @@ build-backend = "setuptools.build_meta" [project] name = "openenv-coding-agent-env" version = "0.1.0" -description = "Coding-agent environment for OpenEnv — runs OpenCode/Pi harnesses in an E2B sandbox against OpenAI-compatible LLM endpoints, optionally capturing per-token logprobs." +description = "Coding-agent environment for OpenEnv — runs OpenCode/Pi harnesses in an E2B sandbox against OpenAI-compatible LLM endpoints." requires-python = ">=3.10" dependencies = [ # Core OpenEnv (server + MCP). 0.3.0 ships the harness runtime. @@ -26,7 +26,7 @@ dependencies = [ # behavior drift on Space rebuilds. "gradio>=6.0.0", - # OpenCode harness primitive — sandbox + proxy + agent driver + # OpenCode harness primitive — sandbox + agent driver "httpx>=0.27.0", "e2b>=1.0.0", ] diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index ceee49002..af70b292e 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -15,8 +15,8 @@ Reward = ``passed_verify_commands / total`` unless a verify command writes a float to ``/home/user/logs/verifier/reward.txt`` (override). -Returns a JSON-serialized :class:`RolloutResult` with reward + per-turn -logprobs (Mode B) + setup/verify command results + file outputs. +Returns a JSON-serialized :class:`RolloutResult` with reward, +setup/verify command results, and file outputs. """ from __future__ import annotations @@ -184,6 +184,11 @@ def run_rollout( raise ValueError( f"unsupported agent {agent!r}; supported agents: {_SUPPORTED_AGENTS}" ) + if mode not in {"black_box", "interception_gate"}: + raise ValueError( + "unsupported mode {!r}; supported modes: ('black_box', " + "'interception_gate')".format(mode) + ) if not (base_url and api_key and model): raise ValueError( "must provide either ``endpoint`` (one of " @@ -303,6 +308,12 @@ def _emit(msg: str) -> None: except Exception: pass + if mode not in {"black_box", "interception_gate"}: + raise ValueError( + "unsupported mode {!r}; supported modes: ('black_box', " + "'interception_gate')".format(mode) + ) + result = self._RolloutResult(task_id=task_id, mode=mode) t0 = time.time() @@ -347,18 +358,17 @@ def _emit(msg: str) -> None: metadata={"task_id": task_id, "agent": agent}, ) - factory = self._build_session_factory( - agent=agent, - config=config, - mode=mode, - template=template, - disable_thinking=disable_thinking, - top_logprobs=top_logprobs, - max_tokens_cap=max_tokens_cap, - ) - session = None try: + factory = self._build_session_factory( + agent=agent, + config=config, + mode=mode, + template=template, + disable_thinking=disable_thinking, + top_logprobs=top_logprobs, + max_tokens_cap=max_tokens_cap, + ) _emit( f"creating E2B sandbox (template={template or 'default'}) — " "this is the slow phase (~5–60s cold, ~5s with template)" @@ -367,24 +377,22 @@ def _emit(msg: str) -> None: result.sandbox_id = session.sandbox.sandbox_id _emit(f"sandbox ready: {result.sandbox_id} — agent started (mode={mode})") - # Re-run setup commands individually for per-command - # observability in the response. The commands already ran - # atomically via setup_shell above, so these re-runs are - # idempotent — they exist only to populate - # result.setup_results with per-command exit/stdout/stderr. - for i, cmd in enumerate(setup, 1): - cr = self._exec_command(session.sandbox, cmd) - result.setup_results.append(cr) - if cr.exit_code != 0: - # Should not happen — setup_shell already succeeded - # during bootstrap, but record it for diagnostics. - result.error = ( - f"setup replay failed (exit {cr.exit_code}): {cmd[:120]}" + # setup commands already ran atomically during sandbox bootstrap. + # Avoid re-running them here because many setup scripts are not + # idempotent (e.g., migrations, one-shot installs, destructive prep). + # We still surface per-command bookkeeping for callers. + for cmd in setup: + result.setup_results.append( + self._CommandResult( + cmd=cmd, + exit_code=0, + stdout="executed during bootstrap", + stderr="", + duration_s=0.0, ) - _emit(f"setup replay FAILED at [{i}]: exit={cr.exit_code}") - break + ) - # Block until the agent is done (or setup already failed). + # Block until the agent is done. if result.error is None: _emit( f"agent running — {agent} CLI in sandbox " @@ -498,6 +506,11 @@ def _build_session_factory( top_logprobs: int, max_tokens_cap: int, ) -> Any: + if self._E2BSandboxBackend is None: + raise RuntimeError( + "E2BSandboxBackend unavailable: install optional dependency 'e2b'." + ) + backend_kwargs: dict[str, Any] = {} if template: backend_kwargs["template"] = template diff --git a/examples/coding_agent_env_simple.py b/examples/coding_agent_env_simple.py index f8996e586..caf81bad8 100644 --- a/examples/coding_agent_env_simple.py +++ b/examples/coding_agent_env_simple.py @@ -14,12 +14,9 @@ 1. Spawns a fresh E2B sandbox (using the prebaked ``coding-agent-rl`` template — falls back to a cold install if the template isn't present in your E2B account). - 2. Bootstraps an in-sandbox FastAPI proxy that captures per-token - logprobs (``mode="transparent_proxy"``). - 3. Runs the selected harness CLI with the instruction. - 4. Executes the verify bash commands; reward = passed / total. - 5. Returns a ``RolloutResult`` with reward + per-turn logprobs + - the file contents the agent produced. + 2. Runs the selected harness CLI with the instruction. + 3. Executes the verify bash commands; reward = passed / total. + 4. Returns a ``RolloutResult`` with reward + produced file contents. Prerequisites ------------- @@ -34,7 +31,6 @@ Expected output (~20s with the prebaked template):: reward: 1.0 - turns: 3 files: ['/home/user/workdir/binary_search.py', ...] wall: 19.8 s """ @@ -54,7 +50,9 @@ from coding_agent_env.models import RolloutResult # noqa: E402 -SPACE = os.environ.get("CODING_AGENT_ENV_SPACE", "https://adithyask-coding-agent-env.hf.space") +SPACE = os.environ.get( + "CODING_AGENT_ENV_SPACE", "https://adithyask-coding-agent-env.hf.space" +) INSTRUCTION = ( "Create a single Python file named `binary_search.py` in the current " @@ -109,8 +107,6 @@ async def main() -> int: print("--- result ---") print(f"reward: {result.reward}") - print(f"turns: {len(result.proxy_turns)}") - print(f"tokens: {sum(len(t.completion_tokens) for t in result.proxy_turns)}") print(f"sandbox: {result.sandbox_id}") print(f"wall_s: {result.wall_s}") print(f"files: {sorted(result.files)}") @@ -118,16 +114,6 @@ async def main() -> int: if result.error: print(f"error: {result.error}") - if result.proxy_turns: - first = next((t for t in result.proxy_turns if t.completion_tokens), None) - if first: - print() - print("--- first productive turn (first 8 tokens with logprobs) ---") - toks = first.completion_tokens[:8] - lps = first.per_token_logps[:8] - for tok, lp in zip(toks, lps): - print(f" {tok!r:<14} {lp:+.3f}") - return 0 if result.reward == 1.0 else 1 diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 1d934777d..42161bfec 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -21,7 +21,6 @@ import json import logging import shlex -import threading import time import uuid from typing import Any, Callable, Literal @@ -117,11 +116,14 @@ def close(self) -> None: def wait_for_completion(self, timeout_s: float | None = None) -> int: """Block until the agent exits, returning its exit code.""" - budget = timeout_s if timeout_s is not None else self.spec.default_timeout_s - if hasattr(self.config, "agent_timeout_s"): - budget = timeout_s if timeout_s is not None else self.config.agent_timeout_s if self._agent_bg_job is None: raise RuntimeError("Agent not started.") + default_timeout = ( + self.config.agent_timeout_s + if hasattr(self.config, "agent_timeout_s") + else self.spec.default_timeout_s + ) + budget = timeout_s if timeout_s is not None else default_timeout return self._agent_bg_job.wait(timeout=budget) def collect_artifacts(self) -> dict[str, Any]: @@ -189,17 +191,19 @@ async def next_request( self._interception_queue.get(), timeout=min(remaining, 1.0), ) - return server.intercepts[request_id] + intercept = server.get_intercept(request_id) + if intercept is not None: + return intercept except asyncio.TimeoutError: - if self._agent_bg_job is not None: - done_event = getattr(self._agent_bg_job, "_done", None) - if ( - done_event is not None - and isinstance(done_event, threading.Event) - and done_event.is_set() - ): - return None - continue + pass + + if self._agent_bg_job is not None: + try: + self._agent_bg_job.wait(timeout=0) + return None + except TimeoutError: + pass + continue async def deliver( self, intercept: dict[str, Any], response_dict: dict[str, Any] @@ -241,6 +245,14 @@ def __init__( self._interception_server = interception_server self._interception_base_url = interception_base_url + def bootstrap_sandbox(self, sandbox: SandboxHandle, task: Any, config: Any) -> None: + """Public bootstrap hook used by external wrappers. + + Runs readiness checks, optional install, file upload, MCP config write, + and task setup shell execution. + """ + self._bootstrap_sandbox(sandbox, task, config) + def create_session( self, task: Any, diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index a075ec8b4..1aa4edf57 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -36,7 +36,9 @@ while True: request_id = await asyncio.wait_for(queue.get(), timeout=...) - intercept = server.intercepts[request_id] + intercept = server.get_intercept(request_id) + if intercept is None: + continue response = await vllm.generate(intercept["messages"], ...) await deliver_response(intercept, response) @@ -51,6 +53,7 @@ import json import logging import secrets +import threading import time import uuid from typing import Any @@ -71,13 +74,20 @@ class InterceptionServer: identified by a ``rollout_id`` in the URL path. """ - def __init__(self, port: int = 0, secret: str | None = None) -> None: + def __init__( + self, + port: int = 0, + secret: str | None = None, + host: str = "127.0.0.1", + ) -> None: self.port = port + self.host = host self.secret = secret or secrets.token_urlsafe(32) self._app: web.Application | None = None self._runner: web.AppRunner | None = None self._site: web.TCPSite | None = None self._lock = asyncio.Lock() + self._state_lock = threading.RLock() self.active_rollouts: dict[str, dict[str, Any]] = {} self.intercepts: dict[str, dict[str, Any]] = {} @@ -93,7 +103,9 @@ async def start(self) -> None: app.router.add_get("/health", self._handle_health) runner = web.AppRunner(app) await runner.setup() - site = web.TCPSite(runner, "0.0.0.0", self.port) + if self.host == "0.0.0.0": + _log.warning("InterceptionServer exposed on all interfaces (0.0.0.0).") + site = web.TCPSite(runner, self.host, self.port) await site.start() if self.port == 0: server = getattr(site, "_server", None) @@ -111,7 +123,11 @@ async def stop(self) -> None: async with self._lock: if self._runner is None: return - for intercept in list(self.intercepts.values()): + with self._state_lock: + intercepts = list(self.intercepts.values()) + self.intercepts.clear() + self.active_rollouts.clear() + for intercept in intercepts: fut: asyncio.Future | None = intercept.get("response_future") if fut and not fut.done(): fut.cancel() @@ -121,8 +137,6 @@ async def stop(self) -> None: cq.put_nowait(None) except asyncio.QueueFull: pass - self.intercepts.clear() - self.active_rollouts.clear() try: await self._runner.cleanup() except RuntimeError: @@ -137,27 +151,39 @@ def register_rollout( state: dict[str, Any] | None = None, ) -> asyncio.Queue: queue: asyncio.Queue = asyncio.Queue() - self.active_rollouts[rollout_id] = { - "request_id_queue": queue, - "state": state, - } + with self._state_lock: + self.active_rollouts[rollout_id] = { + "request_id_queue": queue, + "state": state, + } return queue def unregister_rollout(self, rollout_id: str) -> None: - for request_id in list(self.intercepts): - intercept = self.intercepts.get(request_id) - if intercept and intercept.get("rollout_id") == rollout_id: - fut: asyncio.Future | None = intercept.get("response_future") - if fut and not fut.done(): - fut.cancel() - cq: asyncio.Queue | None = intercept.get("chunk_queue") - if cq is not None: - try: - cq.put_nowait(None) - except asyncio.QueueFull: - pass + with self._state_lock: + matching_ids = [ + request_id + for request_id, intercept in self.intercepts.items() + if intercept.get("rollout_id") == rollout_id + ] + matching_intercepts = [self.intercepts[i] for i in matching_ids] + for request_id in matching_ids: del self.intercepts[request_id] - self.active_rollouts.pop(rollout_id, None) + self.active_rollouts.pop(rollout_id, None) + + for intercept in matching_intercepts: + fut: asyncio.Future | None = intercept.get("response_future") + if fut and not fut.done(): + fut.cancel() + cq: asyncio.Queue | None = intercept.get("chunk_queue") + if cq is not None: + try: + cq.put_nowait(None) + except asyncio.QueueFull: + pass + + def get_intercept(self, request_id: str) -> dict[str, Any] | None: + with self._state_lock: + return self.intercepts.get(request_id) def _authorized(self, request: web.Request) -> bool: auth = request.headers.get("Authorization", "") @@ -176,7 +202,8 @@ async def _handle_chat_completions( return web.json_response({"error": "Unauthorized"}, status=401) rollout_id = request.match_info["rollout_id"] - context = self.active_rollouts.get(rollout_id) + with self._state_lock: + context = self.active_rollouts.get(rollout_id) if not context: return web.json_response({"error": "rollout not found"}, status=404) @@ -197,11 +224,16 @@ async def _handle_chat_completions( "tools": body.get("tools"), "stream": is_streaming, "chunk_queue": chunk_queue, - "response_future": asyncio.get_event_loop().create_future(), + "response_future": asyncio.get_running_loop().create_future(), "body": body, } - self.intercepts[request_id] = intercept - await context["request_id_queue"].put(request_id) + with self._state_lock: + context = self.active_rollouts.get(rollout_id) + if context is None: + return web.json_response({"error": "rollout not found"}, status=404) + self.intercepts[request_id] = intercept + request_queue: asyncio.Queue = context["request_id_queue"] + await request_queue.put(request_id) if is_streaming: return await self._stream_response(request, intercept) @@ -210,8 +242,12 @@ async def _handle_chat_completions( response_dict = await intercept["response_future"] except asyncio.CancelledError: return web.json_response({"error": "rollout cancelled"}, status=499) - except Exception as exc: - return web.json_response({"error": str(exc)}, status=500) + except Exception: + _log.exception("interception request %s failed", request_id) + return web.json_response({"error": "internal error"}, status=500) + finally: + with self._state_lock: + self.intercepts.pop(request_id, None) return web.json_response(response_dict) @@ -249,6 +285,13 @@ async def _stream_response( finally: if get_task and not get_task.done(): get_task.cancel() + fut: asyncio.Future | None = intercept.get("response_future") + if fut and not fut.done(): + fut.cancel() + request_id = intercept.get("request_id") + if isinstance(request_id, str): + with self._state_lock: + self.intercepts.pop(request_id, None) try: await resp.write_eof() except Exception: diff --git a/src/openenv/core/harness/agents/opencode.py b/src/openenv/core/harness/agents/opencode.py index 13c17fa04..9a829c3e2 100644 --- a/src/openenv/core/harness/agents/opencode.py +++ b/src/openenv/core/harness/agents/opencode.py @@ -19,6 +19,7 @@ from __future__ import annotations import json +import shlex from typing import Any from . import register_agent @@ -39,11 +40,15 @@ def _build_opencode_command( log_file = f"{home}/logs/agent/opencode.jsonl" workdir = f"{home}/workdir" + workdir_q = shlex.quote(workdir) + instruction_q = shlex.quote(instruction_file) + log_q = shlex.quote(log_file) + return ( f'export PATH="$HOME/.opencode/bin:$PATH" && ' - f"cd {workdir} && git init -q 2>/dev/null; " - f'opencode run {format_flag} "$(cat {instruction_file})" ' - f"2>&1 | tee {log_file}" + f"cd {workdir_q} && git init -q 2>/dev/null; " + f'opencode run {format_flag} "$(cat {instruction_q})" ' + f"2>&1 | tee {log_q}" ).strip() diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py index d7b60569f..dcc552842 100644 --- a/src/openenv/core/harness/agents/pi.py +++ b/src/openenv/core/harness/agents/pi.py @@ -61,12 +61,16 @@ def _build_command( if hasattr(config, "thinking") and config.thinking: thinking = f" --thinking {shlex.quote(config.thinking)}" + workdir_q = shlex.quote(workdir) + instruction_q = shlex.quote(instruction_file) + log_q = shlex.quote(log_file) + return ( - f"cd {workdir} && git init -q 2>/dev/null; " + f"cd {workdir_q} && git init -q 2>/dev/null; " f"pi --no-session --no-context-files" f"{provider}{model}{thinking}" - f" -p @{instruction_file}" - f" 2>&1 | tee {log_file}" + f" -p @{instruction_q}" + f" 2>&1 | tee {log_q}" ) diff --git a/src/openenv/core/harness/sandbox/_util.py b/src/openenv/core/harness/sandbox/_util.py new file mode 100644 index 000000000..6291b0fb3 --- /dev/null +++ b/src/openenv/core/harness/sandbox/_util.py @@ -0,0 +1,12 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +from __future__ import annotations + + +def shell_quote(s: str) -> str: + """Single-quote a string for shell, escaping embedded single quotes.""" + return "'" + s.replace("'", "'\\''") + "'" diff --git a/src/openenv/core/harness/sandbox/docker_backend.py b/src/openenv/core/harness/sandbox/docker_backend.py index 559817d1b..28447ce2e 100644 --- a/src/openenv/core/harness/sandbox/docker_backend.py +++ b/src/openenv/core/harness/sandbox/docker_backend.py @@ -31,6 +31,7 @@ import uuid from pathlib import PurePosixPath +from openenv.core.harness.sandbox._util import shell_quote from openenv.core.harness.sandbox.base import BgJob, ExecResult _log = logging.getLogger(__name__) @@ -45,12 +46,14 @@ class DockerBgJob: """ def __init__( - self, container_id: str, pid: int, poll_thread: threading.Thread + self, + container_id: str, + pid: int, + poll_thread: threading.Thread | None = None, ) -> None: self._container_id = container_id self._pid = pid self._exit_code: int | None = None - self._error: BaseException | None = None self._done = threading.Event() self._poll_thread = poll_thread @@ -63,8 +66,6 @@ def wait(self, timeout: float | None = None) -> int: raise TimeoutError( f"Background command (pid={self._pid}) did not exit within {timeout}s" ) - if self._error is not None: - raise self._error return self._exit_code if self._exit_code is not None else 0 def kill(self) -> None: @@ -127,8 +128,8 @@ def start_bg( envs: dict[str, str] | None = None, cwd: str | None = None, ) -> BgJob: - marker = f"/tmp/.bg_{uuid.uuid4().hex[:8]}" - wrapped = f"bash -c {_shell_quote(cmd + f'; echo $? > {marker}')} &\necho $!" + marker = f"/tmp/.bg_{uuid.uuid4().hex}" + wrapped = f"bash -c {shell_quote(cmd + f'; echo $? > {marker}')} &\necho $!" docker_cmd = self._build_exec_cmd(envs=envs, cwd=cwd) docker_cmd.extend(["bash", "-c", wrapped]) result = subprocess.run(docker_cmd, capture_output=True, text=True, timeout=10) @@ -147,7 +148,7 @@ def start_bg( ) pid = int(pid_line) - job = DockerBgJob(self._container_id, pid, poll_thread=None) # type: ignore[arg-type] + job = DockerBgJob(self._container_id, pid) poll_thread = threading.Thread( target=self._poll_bg_job, args=(job, marker), @@ -174,7 +175,7 @@ def write_text(self, path: str, content: str) -> None: self._container_id, "bash", "-c", - f"cat > {_shell_quote(path)}", + f"cat > {shell_quote(path)}", ], input=content.encode(), capture_output=True, @@ -233,6 +234,7 @@ def _build_exec_cmd( return cmd def _poll_bg_job(self, job: DockerBgJob, marker: str) -> None: + consecutive_failures = 0 while not job._done.is_set(): try: result = subprocess.run( @@ -245,22 +247,38 @@ def _poll_bg_job(self, job: DockerBgJob, marker: str) -> None: job._exit_code = int(result.stdout.strip()) job._done.set() return + if "No such container" in (result.stderr or ""): + job._exit_code = 1 + job._done.set() + return except Exception: - pass + consecutive_failures += 1 + else: + consecutive_failures = 0 # Also check if PID is gone (crash without writing marker). try: check = subprocess.run( ["docker", "exec", self._container_id, "kill", "-0", str(job._pid)], capture_output=True, + text=True, timeout=5, ) if check.returncode != 0: job._exit_code = 1 job._done.set() return + if "No such container" in (check.stderr or ""): + job._exit_code = 1 + job._done.set() + return except Exception: - pass + consecutive_failures += 1 + + if consecutive_failures >= 10: + job._exit_code = 1 + job._done.set() + return time.sleep(0.5) @@ -332,8 +350,3 @@ def create( "Docker sandbox created: %s (image=%s)", container_id[:12], self._image ) return DockerSandboxHandle(container_id, user=self._user) - - -def _shell_quote(s: str) -> str: - """Single-quote a string for shell, escaping embedded single quotes.""" - return "'" + s.replace("'", "'\\''") + "'" diff --git a/src/openenv/core/harness/sandbox/hf_backend.py b/src/openenv/core/harness/sandbox/hf_backend.py index bb41356e2..43ec5ad95 100644 --- a/src/openenv/core/harness/sandbox/hf_backend.py +++ b/src/openenv/core/harness/sandbox/hf_backend.py @@ -20,6 +20,7 @@ from typing import Any from hf_sandbox import Sandbox +from openenv.core.harness.sandbox._util import shell_quote from openenv.core.harness.sandbox.base import BgJob, ExecResult, SandboxHandle _ENV_KEY_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") @@ -67,7 +68,7 @@ def wait(self, timeout: float | None = None) -> int: ) marker = self._sandbox.exec( - f"cat {_shell_quote(self._marker_path)}", + f"cat {shell_quote(self._marker_path)}", timeout=10, ) if marker.exit_code == 0 and marker.stdout.strip(): @@ -152,8 +153,8 @@ def start_bg( cwd: str | None = None, ) -> BgJob: marker_path = f"/tmp/.openenv_bg_{uuid.uuid4().hex[:12]}.exit" - wrapped = f"{cmd}; rc=$?; echo $rc > {_shell_quote(marker_path)}" - launch_cmd = f"nohup bash -lc {_shell_quote(wrapped)} >/dev/null 2>&1 & echo $!" + wrapped = f"{cmd}; rc=$?; echo $rc > {shell_quote(marker_path)}" + launch_cmd = f"nohup bash -lc {shell_quote(wrapped)} >/dev/null 2>&1 & echo $!" result = self.exec(launch_cmd, envs=envs, cwd=cwd, timeout=30) if result.exit_code != 0: @@ -174,7 +175,7 @@ def start_bg( def write_text(self, path: str, content: str) -> None: parent = str(PurePosixPath(path).parent) if parent not in ("", "/"): - r = self.exec(f"mkdir -p {_shell_quote(parent)}", timeout=10) + r = self.exec(f"mkdir -p {shell_quote(parent)}", timeout=10) if r.exit_code != 0: raise RuntimeError( f"Failed to create parent directory {parent!r}: {r.stderr}" @@ -185,7 +186,7 @@ def read_text(self, path: str) -> str: return str(self._sbx.read_file(path, text=True)) def exists(self, path: str) -> bool: - r = self.exec(f"test -e {_shell_quote(path)}", timeout=10) + r = self.exec(f"test -e {shell_quote(path)}", timeout=10) return r.exit_code == 0 def kill(self) -> None: @@ -250,8 +251,8 @@ def create( assert last_error is not None raise HFSandboxCreateError( - f"Failed to create HF sandbox after {self._create_retries} attempts: " - f"{last_error}" + f"Failed to create HF sandbox after {self._create_retries} attempts " + f"({type(last_error).__name__})." ) from last_error @@ -262,7 +263,7 @@ def _with_env_prefix(cmd: str, envs: dict[str, str]) -> str: for key, value in envs.items(): if not _ENV_KEY_RE.match(key): raise ValueError(f"Invalid environment variable name: {key!r}") - parts.append(f"export {key}={_shell_quote(str(value))};") + parts.append(f"export {key}={shell_quote(str(value))};") return " ".join(parts) + f" {cmd}" @@ -296,11 +297,6 @@ def _parse_exit_code(raw: str, *, default: int) -> int: return default -def _shell_quote(s: str) -> str: - """Single-quote a string for shell, escaping embedded single quotes.""" - return "'" + s.replace("'", "'\\''") + "'" - - __all__ = [ "HFBgJob", "HFSandboxBackend", diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index 0b218f19a..af9629970 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -483,6 +483,52 @@ def test_create_session_uploads_task_files(self): assert sbx.written["/extra/data.json"] == '{"key": "value"}' session.close() + def test_opencode_black_box_api_key_stays_out_of_command_argv(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + secret = "sk-test '$(leak)" + config = FakeConfig(api_key=secret) + backend = FakeSandboxBackend() + driver = CLIAgentDriver( + spec=OPENCODE_SPEC, + sandbox_backend=backend, + mode="black_box", + ) + + session = driver.create_session(task=FakeTask(), config=config) + sbx = backend.created[0] + cmd, envs = sbx.bg_commands[-1] + assert secret not in cmd + assert envs is not None + assert envs["OPENAI_API_KEY"] == secret + session.close() + + def test_opencode_interception_gate_uses_server_secret_not_user_key(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + from openenv.core.harness.agents.interception_server import InterceptionServer + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + secret = "sk-test '$(leak)" + config = FakeConfig(api_key=secret) + backend = FakeSandboxBackend() + server = InterceptionServer(port=0, secret="gate-secret") + driver = CLIAgentDriver( + spec=OPENCODE_SPEC, + sandbox_backend=backend, + mode="interception_gate", + interception_server=server, + interception_base_url="http://127.0.0.1:8765", + ) + + session = driver.create_session(task=FakeTask(), config=config) + sbx = backend.created[0] + cmd, envs = sbx.bg_commands[-1] + assert secret not in cmd + assert envs is not None + assert envs["OPENAI_API_KEY"] == "gate-secret" + session.close() + def test_create_session_runs_task_setup_shell(self): from openenv.core.harness.agents.cli_driver import CLIAgentDriver @@ -670,6 +716,32 @@ def test_close_kills_sandbox_and_jobs(self): assert sbx._killed assert session._agent_bg_job is None + @pytest.mark.asyncio + async def test_next_request_handles_missing_intercept_without_keyerror(self): + import asyncio + + from openenv.core.harness.agents.cli_driver import CLIAgentSession + from openenv.core.harness.agents.interception_server import InterceptionServer + + spec = _make_test_spec() + sbx = FakeSandbox() + queue: asyncio.Queue[str] = asyncio.Queue() + await queue.put("req_missing") + + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + agent_bg_job=FakeBgJob(), + interception_server=InterceptionServer(secret="s"), + interception_rollout_id="rollout-1", + interception_queue=queue, + ) + + # Missing request IDs can happen if unregister_rollout races with queue.get(). + assert await session.next_request(timeout_s=0.2) is None + class TestCLIAgentSessionFactory: """Tests for the ResourceSessionFactory wrapper.""" @@ -775,6 +847,25 @@ class OcConfig: assert "--format json" in cmd assert "/home/user/task/instruction.md" in cmd + def test_build_command_quotes_paths(self): + from openenv.core.harness.agents.opencode import OPENCODE_SPEC + + @dataclass + class OcConfig: + sandbox_home: str = "/home/user with space" + run_format: str = "json" + + assert OPENCODE_SPEC.build_command is not None + cmd = OPENCODE_SPEC.build_command( + OPENCODE_SPEC, + OcConfig(), + FakeTask(instruction="Write hello.py"), + None, + ) + assert "cd '/home/user with space/workdir'" in cmd + assert "cat '/home/user with space/task/instruction.md'" in cmd + assert "tee '/home/user with space/logs/agent/opencode.jsonl'" in cmd + def test_build_mcp_config(self): from openenv.core.harness.agents.opencode import OPENCODE_SPEC @@ -888,6 +979,29 @@ def test_opencode_driver_integration(self): session.close() +class TestPiSpec: + def test_build_command_quotes_paths(self): + from openenv.core.harness.agents.pi import PI_SPEC + + @dataclass + class PiConfig: + sandbox_home: str = "/home/user with space" + provider: str = "openai" + model: str = "model/name" + thinking: str = "off" + + assert PI_SPEC.build_command is not None + cmd = PI_SPEC.build_command( + PI_SPEC, + PiConfig(), + FakeTask(instruction="Write hello.py"), + None, + ) + assert "cd '/home/user with space/workdir'" in cmd + assert "-p @'/home/user with space/task/instruction.txt'" in cmd + assert "tee '/home/user with space/logs/agent/pi.txt'" in cmd + + # Env var resolution diff --git a/tests/core/test_docker_sandbox_backend.py b/tests/core/test_docker_sandbox_backend.py index b47f6bd4e..b2eebddd2 100644 --- a/tests/core/test_docker_sandbox_backend.py +++ b/tests/core/test_docker_sandbox_backend.py @@ -289,13 +289,13 @@ def test_factory_creates_docker_backend(self): sandbox.kill() def test_satisfies_sandbox_handle_protocol(self): - from openenv.core.harness.sandbox import SandboxBackend + from openenv.core.harness.sandbox import SandboxHandle from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend backend = DockerSandboxBackend(image="ubuntu:22.04") sandbox = backend.create(timeout_s=60) try: - assert isinstance(sandbox, SandboxBackend) or hasattr(sandbox, "exec") + assert isinstance(sandbox, SandboxHandle) assert hasattr(sandbox, "sandbox_id") assert hasattr(sandbox, "exec") assert hasattr(sandbox, "start_bg") diff --git a/tests/core/test_hf_sandbox_backend.py b/tests/core/test_hf_sandbox_backend.py index cd235c748..9cd94b5d8 100644 --- a/tests/core/test_hf_sandbox_backend.py +++ b/tests/core/test_hf_sandbox_backend.py @@ -150,6 +150,11 @@ def _install_fake_hf_sandbox(monkeypatch) -> None: monkeypatch.setitem(sys.modules, "hf_sandbox", fake_module) +@pytest.fixture(autouse=True) +def _reset_fake_hf_calls() -> None: + _FakeSandboxAPI.calls.clear() + + class TestHFSandboxBackend: def test_exported_from_package(self, monkeypatch): _install_fake_hf_sandbox(monkeypatch) @@ -167,7 +172,6 @@ def test_create_exec_write_read_exists_bg_and_kill(self, monkeypatch): _install_fake_hf_sandbox(monkeypatch) importlib.reload(hf_backend) - _FakeSandboxAPI.calls.clear() monkeypatch.setattr(hf_backend, "Sandbox", _FakeSandboxAPI) backend = hf_backend.HFSandboxBackend( diff --git a/tests/core/test_interception_server.py b/tests/core/test_interception_server.py new file mode 100644 index 000000000..45922849c --- /dev/null +++ b/tests/core/test_interception_server.py @@ -0,0 +1,124 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +from __future__ import annotations + +import asyncio + +import aiohttp +import pytest + +from openenv.core.harness.agents.interception_server import ( + InterceptionServer, + deliver_response, +) + + +@pytest.mark.asyncio +async def test_interception_server_rejects_unauthorized_requests() -> None: + server = InterceptionServer(port=0, secret="secret-token") + await server.start() + try: + async with aiohttp.ClientSession() as client: + resp = await client.post( + f"http://127.0.0.1:{server.port}/rollout/r1/v1/chat/completions", + json={"messages": []}, + ) + assert resp.status == 401 + finally: + await server.stop() + + +@pytest.mark.asyncio +async def test_interception_server_returns_404_for_unknown_rollout() -> None: + server = InterceptionServer(port=0, secret="secret-token") + await server.start() + try: + async with aiohttp.ClientSession() as client: + resp = await client.post( + f"http://127.0.0.1:{server.port}/rollout/missing/v1/chat/completions", + headers={"Authorization": "Bearer secret-token"}, + json={"messages": []}, + ) + assert resp.status == 404 + finally: + await server.stop() + + +@pytest.mark.asyncio +async def test_interception_server_non_stream_roundtrip_cleans_intercept() -> None: + server = InterceptionServer(port=0, secret="secret-token") + await server.start() + queue = server.register_rollout("r1") + try: + async with aiohttp.ClientSession() as client: + request_task = asyncio.create_task( + client.post( + f"http://127.0.0.1:{server.port}/rollout/r1/v1/chat/completions", + headers={"Authorization": "Bearer secret-token"}, + json={ + "messages": [{"role": "user", "content": "hi"}], + "stream": False, + }, + ) + ) + request_id = await asyncio.wait_for(queue.get(), timeout=1.0) + intercept = server.get_intercept(request_id) + assert intercept is not None + + await deliver_response( + intercept, + { + "id": "resp-1", + "model": "test-model", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "hello"}, + "finish_reason": "stop", + } + ], + }, + ) + + resp = await request_task + assert resp.status == 200 + payload = await resp.json() + assert payload["id"] == "resp-1" + + # Request entries should not leak after completion. + assert server.get_intercept(request_id) is None + finally: + server.unregister_rollout("r1") + await server.stop() + + +@pytest.mark.asyncio +async def test_interception_server_unregister_rollout_cancels_pending_request() -> None: + server = InterceptionServer(port=0, secret="secret-token") + await server.start() + queue = server.register_rollout("r1") + try: + async with aiohttp.ClientSession() as client: + request_task = asyncio.create_task( + client.post( + f"http://127.0.0.1:{server.port}/rollout/r1/v1/chat/completions", + headers={"Authorization": "Bearer secret-token"}, + json={ + "messages": [{"role": "user", "content": "hi"}], + "stream": False, + }, + ) + ) + _request_id = await asyncio.wait_for(queue.get(), timeout=1.0) + server.unregister_rollout("r1") + + resp = await request_task + assert resp.status == 499 + payload = await resp.json() + assert payload["error"] == "rollout cancelled" + finally: + await server.stop() diff --git a/tests/envs/test_coding_agent_env.py b/tests/envs/test_coding_agent_env.py index 6626c1c59..bfd37758a 100644 --- a/tests/envs/test_coding_agent_env.py +++ b/tests/envs/test_coding_agent_env.py @@ -165,6 +165,29 @@ def test_catalog_summary_shape() -> None: } <= entry.keys() +def test_run_rollout_rejects_unknown_mode() -> None: + from coding_agent_env.server.coding_environment import CodingAgentEnvironment + + env = CodingAgentEnvironment() + with pytest.raises(ValueError, match="unsupported mode"): + env._run_rollout_impl( + agent="opencode", + base_url="https://api.openai.com/v1", + api_key="sk-test", + model="gpt-4o-mini", + instruction="hello", + setup=[], + verify=[], + task_id="", + mode="legacy_proxy", + disable_thinking=False, + max_tokens_cap=1024, + top_logprobs=5, + agent_timeout_s=30.0, + template="", + ) + + def test_build_agent_config_opencode() -> None: from coding_agent_env.server.coding_environment import CodingAgentEnvironment @@ -218,6 +241,35 @@ def test_build_agent_config_pi() -> None: assert cfg_gate.provider == "huggingface" +def test_build_session_factory_requires_e2b_dependency() -> None: + from coding_agent_env.server.coding_environment import CodingAgentEnvironment + + env = CodingAgentEnvironment() + env._E2BSandboxBackend = None + cfg = env._build_agent_config( + agent="pi", + mode="black_box", + base_url="https://router.huggingface.co/v1", + api_key="hf_xxx", + model="zai-org/GLM-5.1", + agent_timeout_s=180.0, + disable_thinking=False, + top_logprobs=5, + max_tokens_cap=4096, + ) + + with pytest.raises(RuntimeError, match="E2BSandboxBackend unavailable"): + env._build_session_factory( + agent="pi", + config=cfg, + mode="black_box", + template="", + disable_thinking=False, + top_logprobs=5, + max_tokens_cap=4096, + ) + + # --------------------------------------------------------------------------- # Models + task coercion # --------------------------------------------------------------------------- From b18caf229f8341021f9aacb23d4ebe83d2465e34 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 20:39:29 +0530 Subject: [PATCH 22/79] chore: remove unsupported mode checks from CodingAgentEnvironment --- .../server/coding_environment.py | 11 --------- tests/envs/test_coding_agent_env.py | 23 ------------------- 2 files changed, 34 deletions(-) diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index af70b292e..b1e7f47ef 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -184,11 +184,6 @@ def run_rollout( raise ValueError( f"unsupported agent {agent!r}; supported agents: {_SUPPORTED_AGENTS}" ) - if mode not in {"black_box", "interception_gate"}: - raise ValueError( - "unsupported mode {!r}; supported modes: ('black_box', " - "'interception_gate')".format(mode) - ) if not (base_url and api_key and model): raise ValueError( "must provide either ``endpoint`` (one of " @@ -308,12 +303,6 @@ def _emit(msg: str) -> None: except Exception: pass - if mode not in {"black_box", "interception_gate"}: - raise ValueError( - "unsupported mode {!r}; supported modes: ('black_box', " - "'interception_gate')".format(mode) - ) - result = self._RolloutResult(task_id=task_id, mode=mode) t0 = time.time() diff --git a/tests/envs/test_coding_agent_env.py b/tests/envs/test_coding_agent_env.py index bfd37758a..905713e7a 100644 --- a/tests/envs/test_coding_agent_env.py +++ b/tests/envs/test_coding_agent_env.py @@ -165,29 +165,6 @@ def test_catalog_summary_shape() -> None: } <= entry.keys() -def test_run_rollout_rejects_unknown_mode() -> None: - from coding_agent_env.server.coding_environment import CodingAgentEnvironment - - env = CodingAgentEnvironment() - with pytest.raises(ValueError, match="unsupported mode"): - env._run_rollout_impl( - agent="opencode", - base_url="https://api.openai.com/v1", - api_key="sk-test", - model="gpt-4o-mini", - instruction="hello", - setup=[], - verify=[], - task_id="", - mode="legacy_proxy", - disable_thinking=False, - max_tokens_cap=1024, - top_logprobs=5, - agent_timeout_s=30.0, - template="", - ) - - def test_build_agent_config_opencode() -> None: from coding_agent_env.server.coding_environment import CodingAgentEnvironment From bfc730542f899a231060082c3a76475915b01863 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 20:40:47 +0530 Subject: [PATCH 23/79] chore: revert linting for out of scope files --- envs/agent_world_model_env/server/web_ui.py | 4 +- envs/chat_env/models.py | 4 +- envs/chat_env/server/chat_environment.py | 4 +- .../server/coding_tools_env_environment.py | 65 +++---------- envs/coding_tools_env/server/e2b_sandbox.py | 23 +---- envs/coding_tools_env/server/gradio_ui.py | 94 ++++++------------- .../jupyter_env/server/jupyter_environment.py | 10 +- envs/repl_env/server/repl_environment.py | 4 +- .../server/terminus_env_environment.py | 10 +- envs/textarena_env/server/gradio_ui.py | 8 +- 10 files changed, 64 insertions(+), 162 deletions(-) diff --git a/envs/agent_world_model_env/server/web_ui.py b/envs/agent_world_model_env/server/web_ui.py index 09b445d3f..84b10c6b2 100644 --- a/envs/agent_world_model_env/server/web_ui.py +++ b/envs/agent_world_model_env/server/web_ui.py @@ -21,7 +21,9 @@ # Keep in sync with DEFAULT_REWARD_CONFIG in config.py. -_DEFAULT_REWARD_JSON = json.dumps(DEFAULT_REWARD_CONFIG, indent=2) +_DEFAULT_REWARD_JSON = json.dumps( + DEFAULT_REWARD_CONFIG, indent=2 +) def _format_obs_md(payload: dict | None) -> str: diff --git a/envs/chat_env/models.py b/envs/chat_env/models.py index da994cbe3..8bc10f09e 100644 --- a/envs/chat_env/models.py +++ b/envs/chat_env/models.py @@ -55,9 +55,7 @@ class ChatState(State): """State of the ChatEnvironment containing message history.""" history_messages: list[Message] = Field(default_factory=list) - history_tokens: list[list[int]] = Field( - default_factory=list - ) # Same len as messages + history_tokens: list[list[int]] = Field(default_factory=list) # Same len as messages class ChatObservation(Observation): diff --git a/envs/chat_env/server/chat_environment.py b/envs/chat_env/server/chat_environment.py index f66f3e790..90b2d01f0 100644 --- a/envs/chat_env/server/chat_environment.py +++ b/envs/chat_env/server/chat_environment.py @@ -90,9 +90,7 @@ def _coerce_tokens(self, tokens) -> list[int]: def _tokenize_conversation(self, conversation: list[Message]) -> list[int]: """Tokenize a conversation with a chat-template fallback for base tokenizers.""" try: - tokens = self.tokenizer.apply_chat_template( - conversation=conversation, tokenize=True - ) + tokens = self.tokenizer.apply_chat_template(conversation=conversation, tokenize=True) except Exception: # Some tokenizers (e.g. gpt2) do not define `chat_template`. fallback_text = "".join( diff --git a/envs/coding_tools_env/server/coding_tools_env_environment.py b/envs/coding_tools_env/server/coding_tools_env_environment.py index d0ef86675..615e7770f 100644 --- a/envs/coding_tools_env/server/coding_tools_env_environment.py +++ b/envs/coding_tools_env/server/coding_tools_env_environment.py @@ -45,28 +45,16 @@ def bash(command: str, timeout: float | None = 30) -> str: return "Error: environment not reset. Call reset() first." timeout_value = 30 if timeout is None else float(timeout) result = self._sandbox.run_shell(command, timeout_s=timeout_value) - self._record( - "bash", result.ok, result.output, result.error, result.metadata - ) - return ( - result.output - if result.ok - else f"ERROR: {result.error}\n{result.output}".strip() - ) + self._record("bash", result.ok, result.output, result.error, result.metadata) + return result.output if result.ok else f"ERROR: {result.error}\n{result.output}".strip() @mcp.tool - def read( - file_path: str, offset: int | None = None, limit: int | None = None - ) -> str: + def read(file_path: str, offset: int | None = None, limit: int | None = None) -> str: """Read file contents using computer instance.""" if not self._sandbox: return "Error: environment not reset. Call reset() first." - result = self._sandbox.read_file( - file_path=file_path, offset=offset, limit=limit - ) - self._record( - "read", result.ok, result.output, result.error, result.metadata - ) + result = self._sandbox.read_file(file_path=file_path, offset=offset, limit=limit) + self._record("read", result.ok, result.output, result.error, result.metadata) return result.output if result.ok else f"ERROR: {result.error}" @mcp.tool @@ -75,9 +63,7 @@ def write(file_path: str, content: str) -> str: if not self._sandbox: return "Error: environment not reset. Call reset() first." result = self._sandbox.write_file(file_path=file_path, content=content) - self._record( - "write", result.ok, result.output, result.error, result.metadata - ) + self._record("write", result.ok, result.output, result.error, result.metadata) return result.output if result.ok else f"ERROR: {result.error}" @mcp.tool @@ -102,14 +88,10 @@ def edit( updated = original.replace(old_string, new_string) else: updated = original.replace(old_string, new_string, 1) - write_result = self._sandbox.write_file( - file_path=file_path, content=updated - ) + write_result = self._sandbox.write_file(file_path=file_path, content=updated) ok = write_result.ok msg = "edit ok" if ok else "" - self._record( - "edit", ok, msg, write_result.error, {"replace_all": replace_all} - ) + self._record("edit", ok, msg, write_result.error, {"replace_all": replace_all}) return msg if ok else f"ERROR: {write_result.error}" @mcp.tool @@ -147,11 +129,7 @@ def multi_edit(file_path: str, edits: list[dict[str, Any]]) -> str: write_result.error, {"applied": applied}, ) - return ( - f"applied {applied} edits" - if write_result.ok - else f"ERROR: {write_result.error}" - ) + return f"applied {applied} edits" if write_result.ok else f"ERROR: {write_result.error}" @mcp.tool def glob(pattern: str, path: str | None = None) -> str: @@ -159,27 +137,17 @@ def glob(pattern: str, path: str | None = None) -> str: if not self._sandbox: return "Error: environment not reset. Call reset() first." result = self._sandbox.glob_files(pattern=pattern, path=path) - self._record( - "glob", result.ok, result.output, result.error, result.metadata - ) + self._record("glob", result.ok, result.output, result.error, result.metadata) return result.output if result.ok else f"ERROR: {result.error}" @mcp.tool - def grep( - pattern: str, path: str | None = None, include: str | None = None - ) -> str: + def grep(pattern: str, path: str | None = None, include: str | None = None) -> str: """Search for patterns in files.""" if not self._sandbox: return "Error: environment not reset. Call reset() first." result = self._sandbox.grep(pattern=pattern, path=path, include=include) - self._record( - "grep", result.ok, result.output, result.error, result.metadata - ) - return ( - result.output - if result.ok - else f"ERROR: {result.error}\n{result.output}".strip() - ) + self._record("grep", result.ok, result.output, result.error, result.metadata) + return result.output if result.ok else f"ERROR: {result.error}\n{result.output}".strip() @mcp.tool def ls(path: str = ".", ignore: list[str] | None = None) -> str: @@ -209,9 +177,7 @@ def todo_write(todos: list[dict[str, Any]]) -> str: self._record("todo_write", False, "", msg, None) return msg self._state.todos = validated - self._record( - "todo_write", True, f"stored {len(validated)} todos", None, None - ) + self._record("todo_write", True, f"stored {len(validated)} todos", None, None) return f"stored {len(validated)} todos" @mcp.tool @@ -315,8 +281,7 @@ def reset( "sandbox_id": self._state.sandbox_id, "message": "Setup command failed.", "setup_results": [ - entry.model_dump() - for entry in self._state.setup_results + entry.model_dump() for entry in self._state.setup_results ], }, ) diff --git a/envs/coding_tools_env/server/e2b_sandbox.py b/envs/coding_tools_env/server/e2b_sandbox.py index d6f77373b..5833c7ecb 100644 --- a/envs/coding_tools_env/server/e2b_sandbox.py +++ b/envs/coding_tools_env/server/e2b_sandbox.py @@ -94,11 +94,7 @@ def read_file( def write_file(self, file_path: str, content: str) -> ToolResult: try: self._sbx.files.write(file_path, content.encode("utf-8")) - return ToolResult( - ok=True, - output="write ok", - metadata={"bytes": len(content.encode("utf-8"))}, - ) + return ToolResult(ok=True, output="write ok", metadata={"bytes": len(content.encode("utf-8"))}) except Exception as exc: return ToolResult(ok=False, error=f"write failed: {exc}") @@ -115,9 +111,7 @@ def glob_files(self, pattern: str, path: str | None = None) -> ToolResult: if result is None: return ToolResult(ok=False, error=_format_error(execution)) matches = result.get("matches", []) - return ToolResult( - ok=True, output="\n".join(matches), metadata={"matches": matches} - ) + return ToolResult(ok=True, output="\n".join(matches), metadata={"matches": matches}) def list_dir(self, path: str = ".", ignore: list[str] | None = None) -> ToolResult: ignore = ignore or [] @@ -143,15 +137,10 @@ def list_dir(self, path: str = ".", ignore: list[str] | None = None) -> ToolResu if not result.get("ok", False): return ToolResult(ok=False, error=str(result.get("error", "ls failed"))) items = result.get("items", []) - lines = [ - f"{'[dir]' if item['is_dir'] else '[file]'} {item['name']}" - for item in items - ] + lines = [f"{'[dir]' if item['is_dir'] else '[file]'} {item['name']}" for item in items] return ToolResult(ok=True, output="\n".join(lines), metadata={"items": items}) - def grep( - self, pattern: str, path: str | None = None, include: str | None = None - ) -> ToolResult: + def grep(self, pattern: str, path: str | None = None, include: str | None = None) -> ToolResult: root = path or "." code = ( "from pathlib import Path\n" @@ -184,9 +173,7 @@ def grep( if not result.get("ok", False): return ToolResult(ok=False, error=str(result.get("error", "grep failed"))) matches = result.get("matches", []) - return ToolResult( - ok=True, output="\n".join(matches), metadata={"matches": matches} - ) + return ToolResult(ok=True, output="\n".join(matches), metadata={"matches": matches}) def kill(self) -> None: try: diff --git a/envs/coding_tools_env/server/gradio_ui.py b/envs/coding_tools_env/server/gradio_ui.py index 1f3845141..c0d670a99 100644 --- a/envs/coding_tools_env/server/gradio_ui.py +++ b/envs/coding_tools_env/server/gradio_ui.py @@ -105,9 +105,7 @@ def _extract_tool_error(result: dict[str, Any]) -> bool: def _format_status(state: dict[str, Any]) -> str: if not state: - return ( - "**No active session.** Configure setup/verify and click *Reset sandbox*." - ) + return "**No active session.** Configure setup/verify and click *Reset sandbox*." sandbox_id = state.get("sandbox_id") or "—" step_count = state.get("step_count", 0) submitted = state.get("submitted", False) @@ -229,9 +227,9 @@ def state_payload() -> dict[str, Any]: label="edits (JSON array)", language="json", value=( - "[\n" + '[\n' ' {"old_string": "TODO", "new_string": "DONE", "replace_all": false}\n' - "]" + ']' ), lines=8, ) @@ -262,10 +260,10 @@ def state_payload() -> dict[str, Any]: label="todos (JSON array)", language="json", value=( - "[\n" + '[\n' ' {"id":"1","content":"Inspect files",' '"status":"in_progress","priority":"high"}\n' - "]" + ']' ), lines=8, ) @@ -339,33 +337,23 @@ def on_tool_change(tool: str): return [help_md, *updates] tool_dropdown.change( - on_tool_change, - inputs=[tool_dropdown], - outputs=[tool_help, *group_components], + on_tool_change, inputs=[tool_dropdown], outputs=[tool_help, *group_components] ) # ───────── Result rendering helper ───────── - def render_result( - tool: str, raw: dict[str, Any] - ) -> tuple[str, str, str, str, str, list[list[str]]]: + def render_result(tool: str, raw: dict[str, Any]) -> tuple[str, str, str, str, str, list[list[str]]]: text = _extract_tool_text(raw) - is_error = ( - _extract_tool_error(raw) - or text.startswith("ERROR:") - or text.startswith("Error:") - ) + is_error = _extract_tool_error(raw) or text.startswith("ERROR:") or text.startswith("Error:") badge = "❌ error" if is_error else "✅ ok" status_line = f"**{tool}** — {badge}" state = state_payload() return ( - status_line, # output_status - text, # output_view - json.dumps(raw, indent=2), # raw_response - _format_status( - state - ), # state_summary (top + summary panel — same content) + status_line, # output_status + text, # output_view + json.dumps(raw, indent=2), # raw_response + _format_status(state), # state_summary (top + summary panel — same content) json.dumps(state, indent=2, default=str), # state_json - _format_history(state), # history_table + _format_history(state), # history_table ) # ───────── Session handlers ───────── @@ -410,33 +398,21 @@ async def on_close(): async def on_run( tool: str, # bash - bash_command: str, - bash_timeout: float, + bash_command: str, bash_timeout: float, # read - read_path: str, - read_offset: float | None, - read_limit: float | None, + read_path: str, read_offset: float | None, read_limit: float | None, # write - write_path: str, - write_content: str, + write_path: str, write_content: str, # edit - edit_path: str, - edit_old: str, - edit_new: str, - edit_replace_all: bool, + edit_path: str, edit_old: str, edit_new: str, edit_replace_all: bool, # multi_edit - multi_edit_path: str, - multi_edit_json: str, + multi_edit_path: str, multi_edit_json: str, # glob - glob_pattern: str, - glob_path: str, + glob_pattern: str, glob_path: str, # grep - grep_pattern: str, - grep_path: str, - grep_include: str, + grep_pattern: str, grep_path: str, grep_include: str, # ls - ls_path: str, - ls_ignore: str, + ls_path: str, ls_ignore: str, # todo_write todo_json: str, ): @@ -517,26 +493,14 @@ async def on_run( # ───────── Wire up events ───────── all_inputs = [ tool_dropdown, - bash_command, - bash_timeout, - read_path, - read_offset, - read_limit, - write_path, - write_content, - edit_path, - edit_old, - edit_new, - edit_replace_all, - multi_edit_path, - multi_edit_json, - glob_pattern, - glob_path, - grep_pattern, - grep_path, - grep_include, - ls_path, - ls_ignore, + bash_command, bash_timeout, + read_path, read_offset, read_limit, + write_path, write_content, + edit_path, edit_old, edit_new, edit_replace_all, + multi_edit_path, multi_edit_json, + glob_pattern, glob_path, + grep_pattern, grep_path, grep_include, + ls_path, ls_ignore, todo_json, ] all_outputs = [ diff --git a/envs/jupyter_env/server/jupyter_environment.py b/envs/jupyter_env/server/jupyter_environment.py index b7902e5d2..bc622ae22 100644 --- a/envs/jupyter_env/server/jupyter_environment.py +++ b/envs/jupyter_env/server/jupyter_environment.py @@ -348,10 +348,7 @@ def step( ) -> Observation: self._state.step_count += 1 obs = super().step(action, timeout_s=timeout_s, **kwargs) - if ( - self._state.submitted_answer is not None - and self._state.last_reward is not None - ): + if self._state.submitted_answer is not None and self._state.last_reward is not None: obs.done = True obs.reward = self._state.last_reward return obs @@ -364,10 +361,7 @@ async def step_async( ) -> Observation: self._state.step_count += 1 obs = await super().step_async(action, timeout_s=timeout_s, **kwargs) - if ( - self._state.submitted_answer is not None - and self._state.last_reward is not None - ): + if self._state.submitted_answer is not None and self._state.last_reward is not None: obs.done = True obs.reward = self._state.last_reward return obs diff --git a/envs/repl_env/server/repl_environment.py b/envs/repl_env/server/repl_environment.py index 13a759c29..f2e6f5d98 100644 --- a/envs/repl_env/server/repl_environment.py +++ b/envs/repl_env/server/repl_environment.py @@ -272,7 +272,9 @@ def reset( # reset() are treated as equal and don't trigger a redundant rebuild. resolved_model = self._resolve_model(llm_model) has_runtime_llm = self._runtime_controller is not None - model_changed = has_runtime_llm and resolved_model != self._current_llm_model + model_changed = ( + has_runtime_llm and resolved_model != self._current_llm_model + ) token_provided = hf_token is not None if not self.llm_query_fn or model_changed or token_provided: effective_token = ( diff --git a/envs/terminus_env/server/terminus_env_environment.py b/envs/terminus_env/server/terminus_env_environment.py index 03de18baa..c6f9e1c02 100644 --- a/envs/terminus_env/server/terminus_env_environment.py +++ b/envs/terminus_env/server/terminus_env_environment.py @@ -183,10 +183,7 @@ def step( ) -> Observation: self._state.step_count += 1 obs = super().step(action, timeout_s=timeout_s, **kwargs) - if ( - self._state.submitted_answer is not None - and self._state.last_reward is not None - ): + if self._state.submitted_answer is not None and self._state.last_reward is not None: obs.done = True obs.reward = self._state.last_reward return obs @@ -199,10 +196,7 @@ async def step_async( ) -> Observation: self._state.step_count += 1 obs = await super().step_async(action, timeout_s=timeout_s, **kwargs) - if ( - self._state.submitted_answer is not None - and self._state.last_reward is not None - ): + if self._state.submitted_answer is not None and self._state.last_reward is not None: obs.done = True obs.reward = self._state.last_reward return obs diff --git a/envs/textarena_env/server/gradio_ui.py b/envs/textarena_env/server/gradio_ui.py index c9bb88cae..45728fc00 100644 --- a/envs/textarena_env/server/gradio_ui.py +++ b/envs/textarena_env/server/gradio_ui.py @@ -71,9 +71,7 @@ def _sudoku_demo_html() -> str: for col in range(9): value = givens.get((row, col), "") border_right = "3px solid #0f172a" if col in {2, 5} else "1px solid #94a3b8" - border_bottom = ( - "3px solid #0f172a" if row in {2, 5} else "1px solid #94a3b8" - ) + border_bottom = "3px solid #0f172a" if row in {2, 5} else "1px solid #94a3b8" background = "#e2e8f0" if value else "#ffffff" cells.append( f""" @@ -84,7 +82,7 @@ def _sudoku_demo_html() -> str: align-items: center; justify-content: center; font-size: 1.1rem; - font-weight: {"700" if value else "400"}; + font-weight: {'700' if value else '400'}; color: #0f172a; background: {background}; border-right: {border_right}; @@ -107,7 +105,7 @@ def _sudoku_demo_html() -> str: border: 3px solid #0f172a; background: #ffffff; "> - {"".join(cells)} + {''.join(cells)}

Use the Playground tab to reset the game and submit moves in the From c2ca0c8734c510c175968b77ff5383bc56d3be8c Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 20:52:53 +0530 Subject: [PATCH 24/79] feat: host-side tool routing and Pi gate models bootstrap --- src/openenv/core/harness/agents/cli_driver.py | 85 ++++++++++- .../harness/agents/interception_server.py | 136 +++++++++++++++++- tests/core/test_cli_agent_driver.py | 37 +++++ tests/core/test_interception_server.py | 124 ++++++++++++++++ 4 files changed, 379 insertions(+), 3 deletions(-) diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 42161bfec..b499d3c5f 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -36,7 +36,11 @@ from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle from .base import CLIAgentSpec -from .interception_server import deliver_response, InterceptionServer +from .interception_server import ( + deliver_response, + InterceptionServer, + ToolHandler, +) _log = logging.getLogger(__name__) @@ -44,6 +48,19 @@ Verifier = Callable[..., VerifyResult] +class _ConfigOverrideView: + """Read-only attribute view with optional overrides.""" + + def __init__(self, base: Any, **overrides: Any) -> None: + self._base = base + self._overrides = overrides + + def __getattr__(self, name: str) -> Any: + if name in self._overrides: + return self._overrides[name] + return getattr(self._base, name) + + class CLIAgentSession(ResourceSession): """Per-rollout session wrapping one sandbox with one running agent CLI.""" @@ -211,6 +228,25 @@ async def deliver( """Return a trainer-generated response to the waiting agent.""" await deliver_response(intercept, response_dict) + def register_tool_handler( + self, + tool_name: str, + handler: ToolHandler, + *, + tool_definition: dict[str, Any] | None = None, + ) -> None: + """Register a host-side interception tool for this rollout.""" + if self._interception_server is None or self._interception_rollout_id is None: + raise RuntimeError( + "register_tool_handler() is only available in interception_gate mode." + ) + self._interception_server.register_tool_handler( + self._interception_rollout_id, + tool_name, + handler, + tool_definition=tool_definition, + ) + class CLIAgentDriver: """Shared driver for all CLI-based agentic harnesses.""" @@ -415,8 +451,23 @@ def _start_agent( *, base_url_override: str | None = None, ) -> BgJob: + command_config = config + if ( + self.mode == "interception_gate" + and self._interception_server is not None + and self.spec.name == "pi" + and base_url_override + ): + self._write_pi_models_config( + sandbox, + config, + rollout_url=base_url_override, + api_key=self._interception_server.secret, + ) + command_config = _ConfigOverrideView(config, provider="openenv") + if self.spec.build_command is not None: - cmd = self.spec.build_command(self.spec, config, task, None) + cmd = self.spec.build_command(self.spec, command_config, task, None) else: cmd = " ".join(shlex.quote(c) for c in self.spec.base_command) envs = self._resolve_env_vars(config, base_url_override=base_url_override) @@ -425,6 +476,36 @@ def _start_agent( envs["ANTHROPIC_API_KEY"] = self._interception_server.secret return sandbox.start_bg(cmd, envs=envs) + def _write_pi_models_config( + self, + sandbox: SandboxHandle, + config: Any, + *, + rollout_url: str, + api_key: str, + ) -> None: + home = config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" + model = config.model if hasattr(config, "model") else "model" + content = json.dumps( + { + "providers": { + "openenv": { + "baseUrl": rollout_url, + "api": "openai-completions", + "apiKey": api_key, + "compat": { + "supportsDeveloperRole": False, + "supportsReasoningEffort": False, + }, + "models": [{"id": model, "reasoning": False}], + } + } + }, + indent=2, + ) + for path in {f"{home}/.pi/agent/models.json", "/root/.pi/agent/models.json"}: + sandbox.write_text(path, content) + def _resolve_env_vars( self, config: Any, diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index 1aa4edf57..70f8c4247 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -56,7 +56,7 @@ import threading import time import uuid -from typing import Any +from typing import Any, Awaitable, Callable from aiohttp import web @@ -66,6 +66,8 @@ _KEEPALIVE_INTERVAL_S = 3.0 _MAX_REQUEST_BODY = 16 * 1024 * 1024 +ToolHandler = Callable[[dict[str, Any]], Awaitable[dict[str, Any]]] + class InterceptionServer: """Async HTTP server that gates every LLM call from sandboxed agents. @@ -100,6 +102,10 @@ async def start(self) -> None: "/rollout/{rollout_id}/v1/chat/completions", self._handle_chat_completions, ) + app.router.add_post( + "/rollout/{rollout_id}/v1/tools/{tool_name}", + self._handle_tool_call, + ) app.router.add_get("/health", self._handle_health) runner = web.AppRunner(app) await runner.setup() @@ -155,6 +161,8 @@ def register_rollout( self.active_rollouts[rollout_id] = { "request_id_queue": queue, "state": state, + "tool_handlers": {}, + "tool_defs": {}, } return queue @@ -185,6 +193,74 @@ def get_intercept(self, request_id: str) -> dict[str, Any] | None: with self._state_lock: return self.intercepts.get(request_id) + def register_tool_handler( + self, + rollout_id: str, + tool_name: str, + handler: ToolHandler, + *, + tool_definition: dict[str, Any] | None = None, + ) -> None: + """Register a host-side tool handler for a rollout. + + The handler is called by ``POST /rollout/{rollout_id}/v1/tools/{tool_name}`` + with a JSON payload containing ``arguments``. + + Optionally provide ``tool_definition`` (OpenAI tool schema). Registered + schemas are injected into intercepted chat-completion requests for the + rollout when the incoming request does not already include the tool. + """ + with self._state_lock: + context = self.active_rollouts.get(rollout_id) + if context is None: + raise KeyError(f"rollout not found: {rollout_id}") + handlers: dict[str, ToolHandler] = context["tool_handlers"] + handlers[tool_name] = handler + if tool_definition is not None: + tool_defs: dict[str, dict[str, Any]] = context["tool_defs"] + tool_defs[tool_name] = tool_definition + + def unregister_tool_handler(self, rollout_id: str, tool_name: str) -> None: + with self._state_lock: + context = self.active_rollouts.get(rollout_id) + if context is None: + return + handlers: dict[str, ToolHandler] = context.get("tool_handlers", {}) + handlers.pop(tool_name, None) + tool_defs: dict[str, dict[str, Any]] = context.get("tool_defs", {}) + tool_defs.pop(tool_name, None) + + @staticmethod + def _tool_name(tool: dict[str, Any]) -> str | None: + if not isinstance(tool, dict): + return None + function = tool.get("function") + if not isinstance(function, dict): + return None + name = function.get("name") + return name if isinstance(name, str) and name else None + + def _merge_rollout_tools( + self, + tools: Any, + tool_defs: dict[str, dict[str, Any]], + ) -> list[dict[str, Any]] | None: + merged: list[dict[str, Any]] = [] + if isinstance(tools, list): + for tool in tools: + if isinstance(tool, dict): + merged.append(tool) + + existing = { + name for item in merged if (name := self._tool_name(item)) is not None + } + for name, tool in tool_defs.items(): + if name in existing: + continue + merged.append(tool) + + return merged or None + def _authorized(self, request: web.Request) -> bool: auth = request.headers.get("Authorization", "") api_key = request.headers.get("x-api-key", "") @@ -195,6 +271,59 @@ def _authorized(self, request: web.Request) -> bool: async def _handle_health(self, request: web.Request) -> web.Response: return web.json_response({"status": "ok"}) + async def _handle_tool_call(self, request: web.Request) -> web.Response: + if not self._authorized(request): + return web.json_response({"error": "Unauthorized"}, status=401) + + rollout_id = request.match_info["rollout_id"] + tool_name = request.match_info["tool_name"] + with self._state_lock: + context = self.active_rollouts.get(rollout_id) + if context is None: + return web.json_response({"error": "rollout not found"}, status=404) + handlers: dict[str, ToolHandler] = context.get("tool_handlers", {}) + handler = handlers.get(tool_name) + if handler is None: + return web.json_response({"error": "tool not found"}, status=404) + + try: + body = await request.json() + except Exception as exc: + return web.json_response({"error": f"invalid JSON: {exc}"}, status=400) + + arguments_raw: Any + if isinstance(body, dict) and "arguments" in body: + arguments_raw = body.get("arguments") + else: + arguments_raw = body + + if arguments_raw is None: + arguments = {} + elif isinstance(arguments_raw, dict): + arguments = arguments_raw + else: + return web.json_response( + {"error": "tool arguments must be a JSON object"}, + status=400, + ) + + try: + response = await handler(arguments) + except Exception: + _log.exception( + "tool handler failed (rollout=%s, tool=%s)", + rollout_id, + tool_name, + ) + return web.json_response({"error": "tool execution failed"}, status=500) + + if not isinstance(response, dict): + return web.json_response( + {"error": "tool handler must return a JSON object"}, + status=500, + ) + return web.json_response(response) + async def _handle_chat_completions( self, request: web.Request ) -> web.StreamResponse | web.Response: @@ -212,6 +341,11 @@ async def _handle_chat_completions( except Exception as exc: return web.json_response({"error": f"invalid JSON: {exc}"}, status=400) + tool_defs: dict[str, dict[str, Any]] = dict(context.get("tool_defs", {})) + merged_tools = self._merge_rollout_tools(body.get("tools"), tool_defs) + if merged_tools is not None: + body["tools"] = merged_tools + is_streaming = bool(body.get("stream")) request_id = f"req_{uuid.uuid4().hex[:8]}" chunk_queue: asyncio.Queue | None = asyncio.Queue() if is_streaming else None diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index af9629970..11110e34c 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -529,6 +529,43 @@ def test_opencode_interception_gate_uses_server_secret_not_user_key(self): assert envs["OPENAI_API_KEY"] == "gate-secret" session.close() + def test_pi_interception_gate_writes_models_json_and_uses_openenv_provider(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + from openenv.core.harness.agents.interception_server import InterceptionServer + from openenv.core.harness.agents.pi import PI_SPEC + + backend = FakeSandboxBackend() + server = InterceptionServer(port=0, secret="gate-secret") + driver = CLIAgentDriver( + spec=PI_SPEC, + sandbox_backend=backend, + mode="interception_gate", + interception_server=server, + interception_base_url="http://127.0.0.1:8765", + ) + + session = driver.create_session(task=FakeTask(), config=FakeConfig()) + sbx = backend.created[0] + + # Command should force the custom provider backed by models.json. + cmd, _envs = sbx.bg_commands[-1] + assert "--provider openenv" in cmd + + home_models = "/home/user/.pi/agent/models.json" + root_models = "/root/.pi/agent/models.json" + assert home_models in sbx.written + assert root_models in sbx.written + + cfg = json.loads(sbx.written[home_models]) + provider = cfg["providers"]["openenv"] + assert provider["api"] == "openai-completions" + assert provider["apiKey"] == "gate-secret" + assert provider["models"][0]["id"] == "test-model" + assert "/rollout/" in provider["baseUrl"] + assert provider["baseUrl"].endswith("/v1") + + session.close() + def test_create_session_runs_task_setup_shell(self): from openenv.core.harness.agents.cli_driver import CLIAgentDriver diff --git a/tests/core/test_interception_server.py b/tests/core/test_interception_server.py index 45922849c..77d844aff 100644 --- a/tests/core/test_interception_server.py +++ b/tests/core/test_interception_server.py @@ -17,6 +17,22 @@ ) +_ANSWER_TOOL = { + "type": "function", + "function": { + "name": "answer", + "description": "Submit final answer for grading", + "parameters": { + "type": "object", + "properties": { + "answer": {"type": "string"}, + }, + "required": ["answer"], + }, + }, +} + + @pytest.mark.asyncio async def test_interception_server_rejects_unauthorized_requests() -> None: server = InterceptionServer(port=0, secret="secret-token") @@ -122,3 +138,111 @@ async def test_interception_server_unregister_rollout_cancels_pending_request() assert payload["error"] == "rollout cancelled" finally: await server.stop() + + +@pytest.mark.asyncio +async def test_interception_server_tool_endpoint_executes_registered_handler() -> None: + server = InterceptionServer(port=0, secret="secret-token") + await server.start() + server.register_rollout("r1") + seen: dict[str, object] = {} + + async def _handler(arguments: dict) -> dict: + seen["arguments"] = arguments + return {"content": [{"type": "text", "text": "✅"}]} + + server.register_tool_handler("r1", "answer", _handler) + try: + async with aiohttp.ClientSession() as client: + resp = await client.post( + f"http://127.0.0.1:{server.port}/rollout/r1/v1/tools/answer", + headers={"Authorization": "Bearer secret-token"}, + json={"arguments": {"answer": "42"}}, + ) + assert resp.status == 200 + payload = await resp.json() + assert payload["content"][0]["text"] == "✅" + assert seen["arguments"] == {"answer": "42"} + finally: + server.unregister_rollout("r1") + await server.stop() + + +@pytest.mark.asyncio +async def test_interception_server_tool_endpoint_returns_404_for_unknown_tool() -> None: + server = InterceptionServer(port=0, secret="secret-token") + await server.start() + server.register_rollout("r1") + try: + async with aiohttp.ClientSession() as client: + resp = await client.post( + f"http://127.0.0.1:{server.port}/rollout/r1/v1/tools/missing", + headers={"Authorization": "Bearer secret-token"}, + json={"arguments": {}}, + ) + assert resp.status == 404 + finally: + server.unregister_rollout("r1") + await server.stop() + + +@pytest.mark.asyncio +async def test_interception_server_injects_registered_tool_defs_into_intercept() -> ( + None +): + server = InterceptionServer(port=0, secret="secret-token") + await server.start() + queue = server.register_rollout("r1") + + async def _handler(arguments: dict) -> dict: + return {"content": [{"type": "text", "text": str(arguments)}]} + + server.register_tool_handler( + "r1", + "answer", + _handler, + tool_definition=_ANSWER_TOOL, + ) + + try: + async with aiohttp.ClientSession() as client: + request_task = asyncio.create_task( + client.post( + f"http://127.0.0.1:{server.port}/rollout/r1/v1/chat/completions", + headers={"Authorization": "Bearer secret-token"}, + json={ + "messages": [{"role": "user", "content": "grade this"}], + "stream": False, + }, + ) + ) + request_id = await asyncio.wait_for(queue.get(), timeout=1.0) + intercept = server.get_intercept(request_id) + assert intercept is not None + tool_names = { + tool["function"]["name"] + for tool in intercept.get("tools", []) + if isinstance(tool, dict) and isinstance(tool.get("function"), dict) + } + assert "answer" in tool_names + + await deliver_response( + intercept, + { + "id": "resp-1", + "model": "test-model", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "done"}, + "finish_reason": "stop", + } + ], + }, + ) + + resp = await request_task + assert resp.status == 200 + finally: + server.unregister_rollout("r1") + await server.stop() From 1b1d9fbff307fb11f450906778063f3930be7604 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 20:53:56 +0530 Subject: [PATCH 25/79] fix: support configurable Pi workdir for command and MCP config --- src/openenv/core/harness/agents/cli_driver.py | 10 +++--- src/openenv/core/harness/agents/pi.py | 6 +++- tests/core/test_cli_agent_driver.py | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index b499d3c5f..3a6c50f51 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -428,14 +428,14 @@ def _write_mcp_config(self, sandbox: SandboxHandle, config: Any) -> None: self.spec.mcp_config.method == "config_file" and self.spec.mcp_config.path_template ): - workdir = ( - config.sandbox_home + "/workdir" - if hasattr(config, "sandbox_home") - else "/home/user/workdir" - ) home = ( config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" ) + workdir = ( + config.workdir + if hasattr(config, "workdir") and getattr(config, "workdir") + else f"{home}/workdir" + ) mcp_path = self.spec.mcp_config.path_template.format( workdir=workdir, home=home ) diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py index dcc552842..6d553eee4 100644 --- a/src/openenv/core/harness/agents/pi.py +++ b/src/openenv/core/harness/agents/pi.py @@ -49,7 +49,11 @@ def _build_command( home = config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" instruction_file = f"{home}/task/instruction.txt" log_file = f"{home}/logs/agent/pi.txt" - workdir = f"{home}/workdir" + workdir = ( + config.workdir + if hasattr(config, "workdir") and getattr(config, "workdir") + else f"{home}/workdir" + ) provider = "" if hasattr(config, "provider") and config.provider: diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index 11110e34c..d27ca00cd 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -156,6 +156,7 @@ class FakeConfig: model: str = "test-model" agent_timeout_s: float = 300.0 sandbox_home: str = "/home/user" + workdir: str | None = None extra_env: dict[str, str] = field(default_factory=dict) @@ -438,6 +439,20 @@ def test_create_session_full_lifecycle(self): session.close() assert sbx._killed + def test_create_session_honors_configured_workdir_for_mcp_file(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec() + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + config = FakeConfig(workdir="/testbed") + session = driver.create_session(task=FakeTask(), config=config) + + sbx = backend.created[0] + assert "/testbed/mcp.json" in sbx.written + session.close() + def test_create_session_skips_install_when_prebaked(self): from openenv.core.harness.agents.cli_driver import CLIAgentDriver @@ -1038,6 +1053,26 @@ class PiConfig: assert "-p @'/home/user with space/task/instruction.txt'" in cmd assert "tee '/home/user with space/logs/agent/pi.txt'" in cmd + def test_build_command_uses_config_workdir_when_present(self): + from openenv.core.harness.agents.pi import PI_SPEC + + @dataclass + class PiConfig: + sandbox_home: str = "/home/user" + workdir: str = "/testbed" + provider: str = "openai" + model: str = "model/name" + thinking: str = "off" + + assert PI_SPEC.build_command is not None + cmd = PI_SPEC.build_command( + PI_SPEC, + PiConfig(), + FakeTask(instruction="Write hello.py"), + None, + ) + assert "cd /testbed" in cmd + # Env var resolution From 2f52e4858f3cc8647b89f2d5ae9b78855fdfa1e1 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 20:55:24 +0530 Subject: [PATCH 26/79] fix: Docker host gateway mapping and per-create image override --- src/openenv/core/harness/sandbox/base.py | 7 ++++- .../core/harness/sandbox/docker_backend.py | 17 ++++++++-- .../core/harness/sandbox/e2b_backend.py | 2 ++ .../core/harness/sandbox/hf_backend.py | 3 +- tests/core/test_docker_sandbox_backend.py | 31 +++++++++++++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/openenv/core/harness/sandbox/base.py b/src/openenv/core/harness/sandbox/base.py index d84e267e1..22f096310 100644 --- a/src/openenv/core/harness/sandbox/base.py +++ b/src/openenv/core/harness/sandbox/base.py @@ -96,5 +96,10 @@ def create( timeout_s: int = 900, envs: dict[str, str] | None = None, metadata: dict[str, str] | None = None, + image: str | None = None, ) -> SandboxHandle: - """Create and return a new, ready-to-use sandbox.""" + """Create and return a new, ready-to-use sandbox. + + ``image`` is backend-specific and may be ignored by providers that do + not support per-sandbox image selection. + """ diff --git a/src/openenv/core/harness/sandbox/docker_backend.py b/src/openenv/core/harness/sandbox/docker_backend.py index 28447ce2e..a64070a46 100644 --- a/src/openenv/core/harness/sandbox/docker_backend.py +++ b/src/openenv/core/harness/sandbox/docker_backend.py @@ -299,9 +299,16 @@ def __init__( user: str | None = None, ) -> None: self._image = image - self._docker_args = docker_args or [] + self._docker_args = list(docker_args or []) self._user = user + # Linux Docker Engine does not auto-resolve host.docker.internal + # unless we explicitly map it. + if "host.docker.internal:host-gateway" not in self._docker_args: + self._docker_args.extend( + ["--add-host", "host.docker.internal:host-gateway"] + ) + try: subprocess.run( ["docker", "version"], @@ -324,6 +331,7 @@ def create( timeout_s: int = 900, envs: dict[str, str] | None = None, metadata: dict[str, str] | None = None, + image: str | None = None, ) -> DockerSandboxHandle: cmd = [ "docker", @@ -338,7 +346,8 @@ def create( for k, v in (envs or {}).items(): cmd.extend(["-e", f"{k}={v}"]) cmd.extend(self._docker_args) - cmd.extend([self._image, "sleep", str(timeout_s)]) + effective_image = image or self._image + cmd.extend([effective_image, "sleep", str(timeout_s)]) result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) if result.returncode != 0: @@ -347,6 +356,8 @@ def create( ) container_id = result.stdout.strip() _log.info( - "Docker sandbox created: %s (image=%s)", container_id[:12], self._image + "Docker sandbox created: %s (image=%s)", + container_id[:12], + effective_image, ) return DockerSandboxHandle(container_id, user=self._user) diff --git a/src/openenv/core/harness/sandbox/e2b_backend.py b/src/openenv/core/harness/sandbox/e2b_backend.py index 29c9d952d..c0cbf75ba 100644 --- a/src/openenv/core/harness/sandbox/e2b_backend.py +++ b/src/openenv/core/harness/sandbox/e2b_backend.py @@ -184,7 +184,9 @@ def create( timeout_s: int = 900, envs: dict[str, str] | None = None, metadata: dict[str, str] | None = None, + image: str | None = None, ) -> SandboxHandle: + del image sbx = Sandbox.create( template=self._template, timeout=timeout_s, diff --git a/src/openenv/core/harness/sandbox/hf_backend.py b/src/openenv/core/harness/sandbox/hf_backend.py index 43ec5ad95..3857ea7e6 100644 --- a/src/openenv/core/harness/sandbox/hf_backend.py +++ b/src/openenv/core/harness/sandbox/hf_backend.py @@ -228,9 +228,10 @@ def create( timeout_s: int = 900, envs: dict[str, str] | None = None, metadata: dict[str, str] | None = None, + image: str | None = None, ) -> SandboxHandle: # `hf-sandbox` does not support metadata at create-time yet. - del metadata + del metadata, image timeout = self._timeout or _format_timeout(timeout_s) last_error: Exception | None = None diff --git a/tests/core/test_docker_sandbox_backend.py b/tests/core/test_docker_sandbox_backend.py index b2eebddd2..c309e63b3 100644 --- a/tests/core/test_docker_sandbox_backend.py +++ b/tests/core/test_docker_sandbox_backend.py @@ -69,6 +69,37 @@ def test_create_sandbox_backend_unknown_raises(self): with pytest.raises(ValueError, match="Unknown sandbox backend"): create_sandbox_backend("bogus") # type: ignore[arg-type] + def test_create_adds_host_gateway_and_supports_image_override(self, monkeypatch): + import openenv.core.harness.sandbox.docker_backend as docker_backend + + calls: list[list[str]] = [] + + def _fake_run(cmd, *args, **kwargs): + calls.append(list(cmd)) + if cmd[:2] == ["docker", "version"]: + return subprocess.CompletedProcess(cmd, 0, "", "") + if cmd[:2] == ["docker", "run"]: + return subprocess.CompletedProcess( + cmd, + 0, + "1234567890abcdef\n", + "", + ) + return subprocess.CompletedProcess(cmd, 0, "", "") + + monkeypatch.setattr(docker_backend.subprocess, "run", _fake_run) + + backend = docker_backend.DockerSandboxBackend(image="base:latest") + handle = backend.create(image="override:latest") + assert handle.sandbox_id == "1234567890ab" + + run_cmds = [cmd for cmd in calls if cmd[:2] == ["docker", "run"]] + assert len(run_cmds) == 1 + run_cmd = run_cmds[0] + assert "--add-host" in run_cmd + assert "host.docker.internal:host-gateway" in run_cmd + assert "override:latest" in run_cmd + @pytest.mark.skipif(_DOCKER_AVAILABLE, reason="Only test error when Docker missing") def test_backend_raises_without_docker(self): from openenv.core.harness.sandbox.docker_backend import DockerSandboxBackend From f3fede2354f072dc438677eb6d66da5edba4f8ee Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 20:57:00 +0530 Subject: [PATCH 27/79] feat: configurable extension directory support for CLI agents --- src/openenv/core/harness/agents/base.py | 8 ++++++ src/openenv/core/harness/agents/cli_driver.py | 26 +++++++++++++++++++ src/openenv/core/harness/agents/pi.py | 1 + tests/core/test_cli_agent_driver.py | 21 +++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/src/openenv/core/harness/agents/base.py b/src/openenv/core/harness/agents/base.py index ded9ba3b8..4ec1c297a 100644 --- a/src/openenv/core/harness/agents/base.py +++ b/src/openenv/core/harness/agents/base.py @@ -206,6 +206,14 @@ class CLIAgentSpec: resolved from the rollout config at runtime. """ + extension_dir_template: str | None = None + """Optional extension install directory template. + + Receives ``{home}`` substitution at runtime (e.g. + ``"{home}/.pi/agent/extensions"``). Drivers may use this to create + extension directories in the correct sandbox user home. + """ + build_command: Callable[..., str] | None = None """``(spec, config, task, mcp_config_path) -> str`` diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 3a6c50f51..2ff07e4d2 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -352,6 +352,7 @@ def _bootstrap_sandbox( self._wait_for_sandbox_ready(sandbox) if not self._agent_already_installed(sandbox): self._install_agent(sandbox) + self._ensure_extension_dir(sandbox, config) self._upload_files(sandbox, task, config) self._write_mcp_config(sandbox, config) setup_shell = task.setup_shell if hasattr(task, "setup_shell") else None @@ -406,6 +407,31 @@ def _install_agent(self, sandbox: SandboxHandle) -> None: label=f"{self.spec.name} install", ) + def _resolve_sandbox_home(self, sandbox: SandboxHandle, config: Any) -> str: + configured = getattr(config, "sandbox_home", None) + if isinstance(configured, str) and configured.strip(): + return configured + try: + result = sandbox.exec('printf %s "$HOME"', timeout=5) + candidate = (result.stdout or "").strip() + if result.exit_code == 0 and candidate: + return candidate + except Exception: + pass + return "/home/user" + + def _ensure_extension_dir(self, sandbox: SandboxHandle, config: Any) -> None: + template = self.spec.extension_dir_template + if not template: + return + home = self._resolve_sandbox_home(sandbox, config) + extension_dir = template.format(home=home) + result = sandbox.exec(f"mkdir -p {shlex.quote(extension_dir)}", timeout=10) + if result.exit_code != 0: + raise RuntimeError( + f"failed to create extension dir {extension_dir!r}: {result.stderr}" + ) + def _upload_files(self, sandbox: SandboxHandle, task: Any, config: Any) -> None: if not self.spec.files: return diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py index 6d553eee4..03946c552 100644 --- a/src/openenv/core/harness/agents/pi.py +++ b/src/openenv/core/harness/agents/pi.py @@ -144,6 +144,7 @@ def _parse_events(line: str) -> AgentEvent | None: "PI_SKIP_VERSION_CHECK": "1", "PI_TELEMETRY": "0", }, + extension_dir_template="{home}/.pi/agent/extensions", build_command=_build_command, build_mcp_config=_build_mcp_config, parse_events=_parse_events, diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index d27ca00cd..977bf6703 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -213,6 +213,7 @@ def test_cli_agent_spec_minimal(self): assert spec.files is None assert spec.artifacts is None assert spec.env is None + assert spec.extension_dir_template is None assert spec.build_command is None def test_cli_agent_spec_full(self): @@ -453,6 +454,21 @@ def test_create_session_honors_configured_workdir_for_mcp_file(self): assert "/testbed/mcp.json" in sbx.written session.close() + def test_create_session_creates_extension_dir_when_spec_declares_one(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + + spec = _make_test_spec(extension_dir_template="{home}/.agent/extensions") + backend = FakeSandboxBackend() + driver = CLIAgentDriver(spec=spec, sandbox_backend=backend, mode="black_box") + + session = driver.create_session(task=FakeTask(), config=FakeConfig()) + sbx = backend.created[0] + assert any( + cmd.startswith("mkdir -p /home/user/.agent/extensions") + for cmd in sbx.executed + ) + session.close() + def test_create_session_skips_install_when_prebaked(self): from openenv.core.harness.agents.cli_driver import CLIAgentDriver @@ -1073,6 +1089,11 @@ class PiConfig: ) assert "cd /testbed" in cmd + def test_spec_declares_extension_dir_template(self): + from openenv.core.harness.agents.pi import PI_SPEC + + assert PI_SPEC.extension_dir_template == "{home}/.pi/agent/extensions" + # Env var resolution From 9bdee17c99844712b838a4310bf8f88d41001323 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 16 May 2026 21:12:25 +0530 Subject: [PATCH 28/79] refactor: harness and models for host-side grading --- envs/mini_swe_env/extensions/swe-answer.ts | 85 ----- envs/mini_swe_env/extensions/swe-grade.sh | 72 ---- envs/mini_swe_env/harness.py | 378 ++++++--------------- envs/mini_swe_env/models.py | 5 +- envs/mini_swe_env/task_loader_swegym.py | 2 +- 5 files changed, 102 insertions(+), 440 deletions(-) delete mode 100644 envs/mini_swe_env/extensions/swe-answer.ts delete mode 100644 envs/mini_swe_env/extensions/swe-grade.sh diff --git a/envs/mini_swe_env/extensions/swe-answer.ts b/envs/mini_swe_env/extensions/swe-answer.ts deleted file mode 100644 index 3a740029c..000000000 --- a/envs/mini_swe_env/extensions/swe-answer.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -// swe-answer.ts — Pi extension that registers the `answer` tool. -// -// During SWE training, this extension is deployed into the sandbox at -// ~/.pi/agent/extensions/swe-answer.ts so Pi auto-discovers it on startup. -// -// The `answer` tool: -// 1. Runs swe-grade.sh (applies test patch, runs eval, reverts patch). -// 2. Parses stdout for the GRADE line (resolved=true/false). -// 3. Returns "Resolved: true/false" to Pi as a feedback signal. -// 4. Pi either stops (done) or continues if not resolved. -// -// NOTE: This extension does NOT write or read reward.txt. The -// authoritative training reward is computed host-side by -// SWESession.verify() using swebench grading. The result returned -// here is only a fast feedback signal for the agent. -// -// Environment variables (set by the harness): -// SWE_GRADE_SCRIPT — path to swe-grade.sh -// SWE_INSTANCE_ID — task instance id (for logging) - -import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; -import { Type } from "typebox"; -import { execSync } from "child_process"; - -export default function (pi: ExtensionAPI) { - const GRADE_SCRIPT = process.env.SWE_GRADE_SCRIPT || "/home/user/swe-grade.sh"; - const INSTANCE_ID = process.env.SWE_INSTANCE_ID || "unknown"; - - pi.registerTool({ - name: "answer", - label: "Submit Answer", - description: - "Submit your solution for grading. Runs the test suite against your changes " + - "and returns whether the issue is resolved. Call this when you believe your " + - "fix is complete. Returns the grading result (Resolved: true/false).", - parameters: Type.Object({}), - - async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) { - let gradeOutput = ""; - let error: string | null = null; - - try { - gradeOutput = execSync(`bash ${GRADE_SCRIPT}`, { - encoding: "utf-8", - timeout: 300_000, // 5 minutes - stdio: ["pipe", "pipe", "pipe"], - env: { ...process.env }, - }); - } catch (e: any) { - // Grade script may exit non-zero if tests fail — that's expected. - gradeOutput = (e.stdout || "") + "\n" + (e.stderr || ""); - error = e.message?.slice(0, 500) || "grading failed"; - } - - // Parse the GRADE line from stdout. - const gradeLine = gradeOutput.split("\n").find(l => l.startsWith("GRADE:")) || ""; - const resolved = gradeLine.includes("resolved=true"); - - const summary = resolved - ? `✅ Resolved: true` - : `❌ Resolved: false`; - - const details = [ - `Instance: ${INSTANCE_ID}`, - `Resolved: ${resolved}`, - error ? `Error: ${error}` : null, - `--- Grade Output (last 2000 chars) ---`, - gradeOutput.slice(-2000), - ] - .filter(Boolean) - .join("\n"); - - return { - content: [{ type: "text", text: `${summary}\n\n${details}` }], - details: { resolved, instance_id: INSTANCE_ID }, - }; - }, - }); -} diff --git a/envs/mini_swe_env/extensions/swe-grade.sh b/envs/mini_swe_env/extensions/swe-grade.sh deleted file mode 100644 index 2186fe4bf..000000000 --- a/envs/mini_swe_env/extensions/swe-grade.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the BSD-style license found in the -# LICENSE file in the root directory of this source tree. - -# swe-grade.sh — Run swebench-style evaluation in the sandbox. -# -# This script is deployed into the sandbox by SWESessionFactory alongside -# the swe-answer.ts Pi extension. When the agent calls the `answer` tool, -# the extension executes this script and parses stdout for the result. -# -# The script: -# 1. Applies the test_patch (new/changed tests for this task). -# 2. Runs the test command for the repo/version. -# 3. Reverts the test_patch so the working tree is clean. -# 4. Prints the eval exit code so the extension can parse it. -# -# NOTE: This script does NOT write reward.txt. The authoritative reward -# is computed host-side by SWESession.verify() using swebench grading. -# The output here is only used by the answer extension as a fast feedback -# signal for the agent ("Resolved: true/false"). -# -# Environment variables (set by the harness): -# SWE_INSTANCE_ID — e.g. "django__django-11099" -# SWE_TESTBED — working directory, e.g. "/testbed" -# SWE_TEST_PATCH — path to the test patch file -# SWE_EVAL_SCRIPT — path to the swebench eval script -# SWE_LOG_FILE — where to write the eval log - -set -uo pipefail - -TESTBED="${SWE_TESTBED:-/testbed}" -TEST_PATCH="${SWE_TEST_PATCH:-/home/user/swe_test.patch}" -EVAL_SCRIPT="${SWE_EVAL_SCRIPT:-/home/user/swe_eval.sh}" -LOG_FILE="${SWE_LOG_FILE:-/home/user/logs/verifier/eval.log}" - -mkdir -p "$(dirname "$LOG_FILE")" - -cd "$TESTBED" || { echo "ERROR: cannot cd to $TESTBED"; exit 1; } - -# --- Step 1: Apply test patch (if present) --- -if [ -f "$TEST_PATCH" ] && [ -s "$TEST_PATCH" ]; then - echo ">>>>> Applying test patch" - git apply --allow-empty "$TEST_PATCH" 2>&1 || true -fi - -# --- Step 2: Run eval script --- -EVAL_EXIT=0 -if [ -f "$EVAL_SCRIPT" ] && [ -s "$EVAL_SCRIPT" ]; then - echo ">>>>> Running eval script" - bash "$EVAL_SCRIPT" > "$LOG_FILE" 2>&1 || EVAL_EXIT=$? -else - echo ">>>>> No eval script found, skipping" - echo "No eval script" > "$LOG_FILE" -fi - -# --- Step 3: Revert test patch --- -if [ -f "$TEST_PATCH" ] && [ -s "$TEST_PATCH" ]; then - echo ">>>>> Reverting test patch" - git apply --allow-empty -R "$TEST_PATCH" 2>&1 || true -fi - -# --- Step 4: Print result for the answer extension to parse --- -if [ "$EVAL_EXIT" -eq 0 ]; then - echo "GRADE: resolved=true eval_exit=0" -else - echo "GRADE: resolved=false eval_exit=$EVAL_EXIT" -fi - -echo ">>>>> Grade complete (eval_exit=$EVAL_EXIT)" diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index eb316e97f..595ef5ea7 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -4,12 +4,12 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""SWE harness session and session factory (v2 — Pi answer extension). +"""SWE harness session and session factory. Integrates ``mini_swe_env`` with the ``CLIAgentDriver`` / ``ResourceSession`` harness infrastructure. Pi runs in the sandbox with its built-in tools -(bash, edit, write, read, grep, find, ls) plus one extension-registered -tool: ``answer``. +(bash, edit, write, read, grep, find, ls) plus one host-side tool: +``answer``. Session lifecycle:: @@ -27,30 +27,20 @@ print(vr.env_reward) # 1.0 or 0.0 (binary) session.close() -**Reward integrity**: The authoritative reward is computed **host-side** -by ``SWESession.verify()``, which extracts the agent's diff from the -sandbox, runs the swebench eval script in the sandbox, downloads the -test log, and grades it on the host via -``swebench.harness.grading.get_eval_report()``. No ``reward.txt`` is -used — the agent cannot influence the training reward. - -The in-sandbox ``answer`` extension gives the agent a fast feedback -signal ("Resolved: true/false") but this is purely informational and -not used for the training reward. - -The factory handles: - 1. Sandbox creation (per-task Docker image, /testbed ready) - 2. Deploy Pi ``answer`` extension (swe-answer.ts + swe-grade.sh) - 3. Deploy eval script (from swebench via ``make_eval_script``) - 4. Deploy test patch file - 5. Run task setup commands - 6. Agent bootstrap + launch - 7. Interception gate rollout registration (when mode="interception_gate") - -The session handles: - - ``verify()`` — host-side grading via swebench, returns binary VerifyResult - - ``initial_messages()`` — instruction prompt - - Interception gate: ``next_request()`` / ``deliver()`` +**Reward architecture**: The ``answer`` tool is a **host-side tool** +routed through the InterceptionServer's tool routing layer (``/vf/tools``). +When the agent calls ``answer()``, the request goes to the host, which +runs swebench grading (revert test files → apply test_patch → run eval → +grade via ``get_eval_report``), and returns the result to the agent. +This is the same result ``verify()`` returns — one grading path, no +in-sandbox grading infrastructure. + +This matches SWE-Gym's architecture where ``answer`` is server-side code +on the OpenReward platform. + +**Requires core changes** — see ``CORE_CHANGES.md`` for the +InterceptionServer tool routing, models.json, workdir, and Docker +host-IP changes needed in ``openenv.core``. """ from __future__ import annotations @@ -82,22 +72,56 @@ HOME = "/home/user" TESTBED = "/testbed" -EVAL_LOG_FILE = f"{HOME}/logs/verifier/eval.log" - -# Extension + grading script paths in sandbox. -EXTENSION_DIR = f"{HOME}/.pi/agent/extensions" -EXTENSION_PATH = f"{EXTENSION_DIR}/swe-answer.ts" -GRADE_SCRIPT_PATH = f"{HOME}/swe-grade.sh" -EVAL_SCRIPT_PATH = f"{HOME}/swe_eval.sh" -TEST_PATCH_PATH = f"{HOME}/swe_test.patch" VERIFY_TIMEOUT_S = 300 SETUP_TIMEOUT_S = 600 -# Source files for the answer extension and grading script. -_EXTENSIONS_DIR = Path(__file__).parent / "extensions" -_SWE_ANSWER_TS = _EXTENSIONS_DIR / "swe-answer.ts" -_SWE_GRADE_SH = _EXTENSIONS_DIR / "swe-grade.sh" + +# ── SWE instruction template ────────────────────────────────────────────── + +_SWE_INSTRUCTION_TEMPLATE = """ +Consider the following PR description: +{problem_statement} + + + +# Task Instructions + +## Overview +You're a software engineer working on a codebase at /testbed. +Your task is to fix the issue described in the PR description above +by making changes to the source code (non-test files). + +## Important Boundaries +- MODIFY: Regular source code files in /testbed +- DO NOT MODIFY: Tests, configuration files (pyproject.toml, setup.cfg, etc.) + +## Recommended Workflow +1. Analyze the codebase by finding and reading relevant files +2. Create a script to reproduce the issue +3. Edit the source code to resolve the issue +4. Verify your fix works by running your script again +5. Test edge cases to ensure your fix is robust + +## Submitting Your Answer +When you've completed your work and verified your fix, call the `answer` +tool to submit your solution for grading. This runs the test suite and +returns whether the issue is resolved. + +You cannot continue working after submitting — make sure your fix is +tested before calling `answer`. +""" + + +def _wrap_instruction(problem_statement: str) -> str: + """Wrap a problem statement with SWE-Gym-style task instructions. + + Tells the agent about the workflow, boundaries, and crucially + about the ``answer`` tool for submission. + """ + return _SWE_INSTRUCTION_TEMPLATE.format( + problem_statement=problem_statement, + ) @dataclass @@ -130,13 +154,15 @@ class SWESession(CLIAgentSession): """Per-rollout session with SWE-specific verify and reward logic. Extends :class:`CLIAgentSession` with: - - ``verify()`` that grades host-side via ``swebench.harness.grading`` - (never trusts in-sandbox files for reward). + - ``verify()`` — returns the reward produced by the host-side + ``answer`` tool (stored by the InterceptionServer tool handler). - Falls back to running verify commands for legacy tasks. - SWE task metadata. - **Reward integrity**: The training reward is always computed on the - host. The agent cannot influence it by writing files in the sandbox. + **Reward architecture**: The ``answer`` tool runs host-side via + the InterceptionServer's ``/vf/tools`` routing. ``verify()`` simply + returns the reward already computed during the rollout. There is + no separate grading step. """ def __init__( @@ -149,11 +175,16 @@ def __init__( super().__init__(**kwargs) self._swe_task = swe_task self._verify_timeout_s = verify_timeout_s + self._answer_reward: float | None = None # set by host-side answer tool @property def swe_task(self) -> SWETask: return self._swe_task + def set_answer_reward(self, reward: float) -> None: + """Called by the host-side answer tool handler to store the reward.""" + self._answer_reward = reward + def initial_messages(self) -> list[Message]: """Return the SWE instruction as the initial prompt.""" return [{"role": "user", "content": self._swe_task.instruction}] @@ -163,39 +194,26 @@ def verify( transcript: list[Message], final_state: Any | None = None, ) -> VerifyResult: - """Compute the training reward host-side. - - For SWE-Gym tasks (metadata has ``test_patch``, ``FAIL_TO_PASS``, - etc.), the flow is: + """Return the reward computed by the host-side ``answer`` tool. - 1. Revert any test files the agent may have modified back to - ``base_commit`` (anti-reward-hacking). - 2. Apply the task's ``test_patch`` in the sandbox. - 3. Run the eval script in the sandbox, capturing the log. - 4. Revert the ``test_patch``. - 5. Download the log to the host. - 6. Grade on the host via ``swebench.harness.grading.get_eval_report()``. + If the agent called ``answer()`` during the rollout, the + InterceptionServer's tool handler already computed the reward + and stored it via ``set_answer_reward()``. - For legacy tasks with shell ``verify`` commands, falls back to - running them and computing pass ratio. - - If neither path applies, reward defaults to 0.0. + If the agent never called ``answer()`` (timeout, crash), + falls back to verify commands (legacy) or defaults to 0.0. """ - # 1. Try host-side swebench grading (primary path). - grade_result = self._host_side_grade() - if grade_result is not None: + # 1. Primary: reward from host-side answer tool. + if self._answer_reward is not None: return VerifyResult( - env_reward=grade_result.reward, + env_reward=self._answer_reward, done=True, metrics={ "instance_id": self._swe_task.instance_id, - "reward_source": "host_swebench", - "resolved": grade_result.resolved, - "patch_applied": grade_result.patch_applied, + "reward_source": "host_answer_tool", }, artifacts={ "task_id": self._swe_task.task_id, - "tests_status": grade_result.tests_status, }, ) @@ -244,132 +262,19 @@ def verify( }, ) - # 3. No grading source available. + # 3. No reward source — agent didn't call answer, no verify cmds. return VerifyResult( env_reward=0.0, done=True, metrics={ "instance_id": self._swe_task.instance_id, - "reward_source": "default_no_grading", + "reward_source": "default_no_answer", }, artifacts={ "task_id": self._swe_task.task_id, }, ) - def _host_side_grade(self) -> Any: - """Run swebench grading host-side. - - Returns a :class:`GradeResult` or ``None`` if the task lacks - SWE-Gym metadata. - """ - metadata = self._swe_task.metadata - if not metadata: - return None - - required_keys = {"patch", "test_patch", "FAIL_TO_PASS", "version"} - if not required_keys.issubset(metadata.keys()): - return None - - try: - from .grading import grade_from_test_output - from .models import SWEGymTask - - gym_task = SWEGymTask( - instance_id=self._swe_task.instance_id, - repo=self._swe_task.repo, - base_commit=self._swe_task.base_commit, - problem_statement=self._swe_task.instruction, - version=str(metadata["version"]), - patch=str(metadata["patch"]), - test_patch=str(metadata["test_patch"]), - FAIL_TO_PASS=list(metadata["FAIL_TO_PASS"]), - PASS_TO_PASS=list(metadata.get("PASS_TO_PASS", [])), - hints_text=str(metadata.get("hints_text", "")), - created_at=str(metadata.get("created_at", "")), - timeout_s=self._swe_task.timeout_s, - ) - except Exception as exc: - _log.warning("Could not reconstruct SWEGymTask: %s", exc) - return None - - try: - # Step 1: Revert test files to base_commit (anti-reward-hacking). - self._revert_test_files(gym_task) - - # Step 2: Apply the known-good test_patch. - self.sandbox.exec( - f"cd {TESTBED} && git apply --allow-empty {TEST_PATCH_PATH}", - timeout=30, - ) - - # Step 3: Run the eval script, capturing output. - r = self.sandbox.exec( - f"bash {EVAL_SCRIPT_PATH} 2>&1", - cwd=TESTBED, - timeout=self._verify_timeout_s, - ) - test_output = (r.stdout or "") + "\n" + (r.stderr or "") - - # Step 4: Revert the test_patch. - self.sandbox.exec( - f"cd {TESTBED} && git apply --allow-empty -R {TEST_PATCH_PATH}", - timeout=30, - ) - - # Step 5: Extract agent's patch for the grading report. - diff_r = self.sandbox.exec( - f"cd {TESTBED} && git diff HEAD", - timeout=30, - ) - model_patch = diff_r.stdout or "" - - # Step 6: Grade on host. - # Prepend patch-applied marker expected by swebench grading. - test_output = f">>>>> Applied Patch (pred)\n{test_output}" - return grade_from_test_output( - gym_task, - test_output, - model_patch=model_patch if model_patch.strip() else None, - ) - - except Exception as exc: - _log.warning("Host-side grading failed: %s", exc) - return None - - def _revert_test_files(self, gym_task: SWEGymTask) -> None: - """Revert any test files the agent may have modified. - - Uses ``git checkout -- `` for each file in - the test_patch, then removes any new test files the agent added. - This prevents reward hacking by weakening test assertions. - """ - test_patch = gym_task.test_patch - if not test_patch: - return - - # Parse file paths from the test patch. - modified_files: list[str] = [] - for line in test_patch.splitlines(): - if line.startswith("+++ b/"): - path = line[6:] - modified_files.append(path) - elif line.startswith("--- a/"): - path = line[6:] - if path != "/dev/null": - modified_files.append(path) - - if not modified_files: - return - - # Revert each test file to base_commit. - base = gym_task.base_commit - for path in set(modified_files): - self.sandbox.exec( - f"cd {TESTBED} && git checkout {base} -- {path} 2>/dev/null || true", - timeout=10, - ) - # ── Tool-call parsing (kept for backward compatibility) ──────────────────── @@ -429,12 +334,11 @@ def parse_terminal_call(text: str) -> dict[str, Any] | None: class SWESessionFactory(ResourceSessionFactory): - """Creates isolated SWE sessions with Pi answer extension. + """Creates isolated SWE sessions. - Deploys the ``answer`` tool extension + grading script into each - sandbox so Pi gets fast feedback on its submission. The - authoritative training reward is computed host-side by - ``SWESession.verify()``. + The ``answer`` tool is registered as a host-side tool on the + InterceptionServer (via ``/vf/tools``). No in-sandbox grading + scripts or extensions are deployed. Compatible with :func:`build_harness_rollout_func`. """ @@ -489,7 +393,7 @@ def create( seed: int | None = None, episode_id: str | None = None, ) -> SWESession: - """Create one SWE session with answer extension deployed. + """Create one SWE session. ``task`` can be an ``SWETask``, ``SWEGymTask``, or a dict. """ @@ -509,14 +413,12 @@ def create( if episode_id else {"instance_id": swe_task.instance_id} ), - image=swe_task.sandbox_image, ) try: if not swe_task.sandbox_image: self._stage_repo(sandbox, swe_task) - self._deploy_answer_extension(sandbox, swe_task) self._run_setup(sandbox, swe_task) agent_task = self._build_agent_task(swe_task) @@ -584,83 +486,6 @@ def _stage_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: f"git checkout failed (exit {r.exit_code}): {r.stderr[:500]}" ) - def _deploy_answer_extension( - self, sandbox: SandboxHandle, task: SWETask - ) -> None: - """Deploy the Pi answer extension and grading infrastructure. - - Writes into the sandbox: - - swe-answer.ts → ~/.pi/agent/extensions/ (Pi auto-discovers) - - swe-grade.sh → ~/swe-grade.sh - - swe_eval.sh → ~/swe_eval.sh (from swebench, if available) - - swe_test.patch → ~/swe_test.patch (test patch, if available) - """ - sandbox.exec( - f"mkdir -p {EXTENSION_DIR} {HOME}/logs/verifier {HOME}/logs/agent", - timeout=10, - ) - - sandbox.write_text(EXTENSION_PATH, _SWE_ANSWER_TS.read_text()) - - sandbox.write_text(GRADE_SCRIPT_PATH, _SWE_GRADE_SH.read_text()) - sandbox.exec(f"chmod +x {GRADE_SCRIPT_PATH}", timeout=5) - - test_patch = task.metadata.get("test_patch", "") - if test_patch: - sandbox.write_text(TEST_PATCH_PATH, test_patch) - - eval_script = self._generate_eval_script(task) - if eval_script: - sandbox.write_text(EVAL_SCRIPT_PATH, eval_script) - sandbox.exec(f"chmod +x {EVAL_SCRIPT_PATH}", timeout=5) - - # Set environment variables for the grading script. - env_file = f"{HOME}/.swe_env" - env_content = "\n".join([ - f"export SWE_INSTANCE_ID={_shell_quote(task.instance_id)}", - f"export SWE_TESTBED={TESTBED}", - f"export SWE_TEST_PATCH={TEST_PATCH_PATH}", - f"export SWE_EVAL_SCRIPT={EVAL_SCRIPT_PATH}", - f"export SWE_LOG_FILE={EVAL_LOG_FILE}", - f"export SWE_GRADE_SCRIPT={GRADE_SCRIPT_PATH}", - ]) - sandbox.write_text(env_file, env_content) - - sandbox.exec( - f'echo "source {env_file}" >> {HOME}/.bashrc', - timeout=5, - ) - - def _generate_eval_script(self, task: SWETask) -> str | None: - """Try to generate a swebench eval script for this task.""" - metadata = task.metadata - required_keys = {"patch", "test_patch", "FAIL_TO_PASS", "version"} - if not metadata or not required_keys.issubset(metadata.keys()): - return None - - try: - from .grading import make_eval_script - from .models import SWEGymTask - - gym_task = SWEGymTask( - instance_id=task.instance_id, - repo=task.repo, - base_commit=task.base_commit, - problem_statement=task.instruction, - version=str(metadata["version"]), - patch=str(metadata["patch"]), - test_patch=str(metadata["test_patch"]), - FAIL_TO_PASS=list(metadata["FAIL_TO_PASS"]), - PASS_TO_PASS=list(metadata.get("PASS_TO_PASS", [])), - hints_text=str(metadata.get("hints_text", "")), - created_at=str(metadata.get("created_at", "")), - timeout_s=task.timeout_s, - ) - return make_eval_script(gym_task) - except Exception as exc: - _log.debug("Could not generate eval script: %s", exc) - return None - def _run_setup(self, sandbox: SandboxHandle, task: SWETask) -> None: """Run task setup commands in the workspace.""" for cmd in task.setup: @@ -672,9 +497,13 @@ def _run_setup(self, sandbox: SandboxHandle, task: SWETask) -> None: ) def _build_agent_task(self, swe_task: SWETask) -> _SWEAgentTask: - """Convert SWETask into the shape CLIAgentDriver expects.""" + """Convert SWETask into the shape CLIAgentDriver expects. + + Wraps the raw problem statement with SWE-Gym-style instructions + that tell the agent about the ``answer`` tool. + """ return _SWEAgentTask( - instruction=swe_task.instruction, + instruction=_wrap_instruction(swe_task.instruction), setup_shell=None, metadata={ "task_id": swe_task.task_id, @@ -684,23 +513,12 @@ def _build_agent_task(self, swe_task: SWETask) -> _SWEAgentTask: ) -def _shell_quote(s: str) -> str: - """Simple shell quoting for env var values.""" - return "'" + s.replace("'", "'\\''") + "'" - - __all__ = [ "SWEAgentConfig", "SWESession", "SWESessionFactory", + "_wrap_instruction", "parse_terminal_call", - # Filesystem constants (useful for tests). - "EVAL_LOG_FILE", - "EVAL_SCRIPT_PATH", - "EXTENSION_DIR", - "EXTENSION_PATH", - "GRADE_SCRIPT_PATH", "HOME", - "TEST_PATCH_PATH", "TESTBED", ] diff --git a/envs/mini_swe_env/models.py b/envs/mini_swe_env/models.py index 20becb326..188b00624 100644 --- a/envs/mini_swe_env/models.py +++ b/envs/mini_swe_env/models.py @@ -68,9 +68,10 @@ def instance_image(self) -> str: """Docker image name for this task (SWE-Gym convention). Uses the ``xingyaoww/`` namespace with ``sweb.eval.x86_64.`` prefix. - Doubles underscores are replaced per SWE-bench convention. + Double underscores are replaced with ``_s_`` per SWE-Gym convention + (matching ``env-swe-gym/swegym.py``). """ - sanitised = self.instance_id.lower().replace("__", "_1776_") + sanitised = self.instance_id.lower().replace("__", "_s_") return f"xingyaoww/sweb.eval.x86_64.{sanitised}:latest" def to_swe_task(self) -> SWETask: diff --git a/envs/mini_swe_env/task_loader_swegym.py b/envs/mini_swe_env/task_loader_swegym.py index 3593022e9..6f2e1631e 100644 --- a/envs/mini_swe_env/task_loader_swegym.py +++ b/envs/mini_swe_env/task_loader_swegym.py @@ -177,7 +177,7 @@ def get_instance_image(instance_id: str) -> str: ``xingyaoww/sweb.eval.x86_64.:latest`` where ``__`` in the instance id is replaced with ``_1776_``. """ - sanitised = instance_id.lower().replace("__", "_1776_") + sanitised = instance_id.lower().replace("__", "_s_") return f"xingyaoww/sweb.eval.x86_64.{sanitised}:latest" From 341c387b0855ec1a180a0192e9de90e9124ed9f1 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 01:56:13 +0530 Subject: [PATCH 29/79] refactor: SWE-Gym grading to use native test outcomes --- envs/mini_swe_env/__init__.py | 10 +- envs/mini_swe_env/grading.py | 222 +++------------ envs/mini_swe_env/harness.py | 237 +++++++++++++++- envs/mini_swe_env/models.py | 7 +- envs/mini_swe_env/server/swe_environment.py | 141 +++++++--- examples/mini_swe_env/README.md | 177 ++++++++++++ examples/mini_swe_env/run_swe_sample.py | 294 ++++++++++++++++++++ 7 files changed, 848 insertions(+), 240 deletions(-) create mode 100644 examples/mini_swe_env/README.md create mode 100644 examples/mini_swe_env/run_swe_sample.py diff --git a/envs/mini_swe_env/__init__.py b/envs/mini_swe_env/__init__.py index f66ccc002..9dfe7defd 100644 --- a/envs/mini_swe_env/__init__.py +++ b/envs/mini_swe_env/__init__.py @@ -8,7 +8,7 @@ load_swegym_tasks, validate_swegym_task, get_instance_image, ... Grading: - grade_from_log, grade_from_test_output, make_eval_script, GradeResult + grade_from_case_results, GradeResult Environment + client: SWEEnvironment, MiniSWEEnv, SWERolloutResult, ... @@ -37,9 +37,7 @@ from .grading import ( GradeResult, GradingError, - grade_from_log, - grade_from_test_output, - make_eval_script, + grade_from_case_results, ) from .client import MiniSWEEnv @@ -61,9 +59,7 @@ # Grading "GradeResult", "GradingError", - "grade_from_log", - "grade_from_test_output", - "make_eval_script", + "grade_from_case_results", # Server models "MiniSWEEnv", "SWECommandResult", diff --git a/envs/mini_swe_env/grading.py b/envs/mini_swe_env/grading.py index 641e456ac..d09f46a4c 100644 --- a/envs/mini_swe_env/grading.py +++ b/envs/mini_swe_env/grading.py @@ -4,162 +4,31 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Binary grading for SWE-Gym tasks via ``swebench.harness.grading``. +"""Binary grading for SWE-Gym tasks. -Wraps the upstream ``get_eval_report()`` function to produce a binary -reward: **1.0** if the instance is fully resolved (all FAIL_TO_PASS tests -now pass, all PASS_TO_PASS tests still pass), **0.0** otherwise. +This module is intentionally SWE-Gym-native and does not depend on +external repo/version parser maps. -Two entry points: +The grading contract matches SWE-Gym semantics: -1. :func:`grade_from_log` — grade from a test log file (typical - in-sandbox flow: run eval script → parse log → grade). - -2. :func:`grade_from_test_output` — grade from raw test stdout/stderr - (useful when the test runner output is captured directly). - -Both require a :class:`SWEGymTask` for the ground-truth test lists. +- reward = 1.0 iff every FAIL_TO_PASS test passes AND every PASS_TO_PASS + test still passes. +- reward = 0.0 otherwise. """ from __future__ import annotations -import logging -import os -import tempfile from typing import Any from .models import SWEGymTask -_log = logging.getLogger(__name__) - class GradingError(RuntimeError): - """Raised when grading fails due to infrastructure issues.""" - - -# ── Public API ───────────────────────────────────────────────────────────── - - -def grade_from_log( - task: SWEGymTask, - log_path: str, - *, - model_name: str = "agent", - model_patch: str | None = None, -) -> GradeResult: - """Grade a submission using a test log file. - - Args: - task: The SWE-Gym task (provides FAIL_TO_PASS, PASS_TO_PASS, etc.). - log_path: Path to the evaluation log file (output of the - ``swebench`` eval script). - model_name: Identifier for the model producing the patch. - model_patch: The git diff of the agent's changes. If ``None``, - a placeholder is used (``get_eval_report`` only checks - whether the patch field is non-None, the content is not - re-applied). - - Returns: - A :class:`GradeResult` with binary ``reward`` and metadata. - """ - if not os.path.isfile(log_path): - raise GradingError(f"Log file not found: {log_path}") - - test_spec = _task_to_test_spec(task) - prediction = { - "instance_id": task.instance_id, - "model_name_or_path": model_name, - "model_patch": model_patch if model_patch is not None else "placeholder", - } - - from swebench.harness.grading import get_eval_report - - report = get_eval_report( - test_spec=test_spec, - prediction=prediction, - test_log_path=log_path, - include_tests_status=True, - ) - - return _report_to_grade_result(report, task.instance_id) - - -def grade_from_test_output( - task: SWEGymTask, - test_output: str, - *, - model_name: str = "agent", - model_patch: str | None = None, -) -> GradeResult: - """Grade a submission from raw test output text. - - Writes the output to a temporary file and delegates to - :func:`grade_from_log`. - - Args: - task: The SWE-Gym task. - test_output: Combined stdout/stderr from running the eval script. - model_name: Identifier for the model. - model_patch: The git diff (or placeholder). - - Returns: - A :class:`GradeResult`. - """ - with tempfile.NamedTemporaryFile( - mode="w", suffix=".log", delete=False, prefix="swe_grade_" - ) as f: - f.write(test_output) - tmp_path = f.name - - try: - return grade_from_log( - task, - tmp_path, - model_name=model_name, - model_patch=model_patch, - ) - finally: - try: - os.unlink(tmp_path) - except OSError: - pass - - -def make_eval_script(task: SWEGymTask) -> str: - """Generate the swebench evaluation shell script for a task. - - This script is intended to be deployed into the sandbox and run - after the agent applies its patch. The script: - - 1. Applies the ``test_patch`` (new/changed tests from the task). - 2. Runs the test command for the repo/version. - 3. Reverts the ``test_patch`` so the working tree is clean. - - The output is bounded by ``>>>>> Start Test Output`` / ``>>>>> End - Test Output`` markers that ``swebench.harness.grading.get_logs_eval`` - expects. - - Returns: - Shell script content as a string. - """ - test_spec = _task_to_test_spec(task) - - return test_spec.eval_script - - -# ── GradeResult ──────────────────────────────────────────────────────────── + """Raised when grading input is invalid.""" class GradeResult: - """Result of grading a SWE-Gym submission. - - Attributes: - reward: Binary reward (1.0 if resolved, 0.0 otherwise). - resolved: Whether the instance was fully resolved. - patch_applied: Whether the agent's patch was successfully applied. - tests_status: Detailed per-test status (if available). - instance_id: The task's instance id. - """ + """Result of grading a SWE-Gym submission.""" __slots__ = ( "reward", @@ -191,63 +60,52 @@ def __repr__(self) -> str: ) -# ── Internal helpers ─────────────────────────────────────────────────────── - - -def _task_to_test_spec(task: SWEGymTask) -> Any: - """Convert a :class:`SWEGymTask` to a ``swebench`` ``TestSpec``.""" - from swebench.harness.test_spec.test_spec import make_test_spec +def grade_from_case_results( + task: SWEGymTask, + case_results: dict[str, bool], + *, + patch_applied: bool = True, +) -> GradeResult: + """Grade directly from per-test-case outcomes. - # Build the instance dict that make_test_spec expects. - instance: dict[str, Any] = { - "instance_id": task.instance_id, - "repo": task.repo, - "base_commit": task.base_commit, - "version": task.version, - "patch": task.patch, - "test_patch": task.test_patch, - "problem_statement": task.problem_statement, - "hints_text": task.hints_text, - "FAIL_TO_PASS": list(task.FAIL_TO_PASS), - "PASS_TO_PASS": list(task.PASS_TO_PASS), - } + Args: + task: SWE-Gym task with FAIL_TO_PASS and PASS_TO_PASS lists. + case_results: Mapping ``test_case -> passed``. + patch_applied: Whether test patch was successfully applied. + """ - return make_test_spec(instance) + if not isinstance(case_results, dict): + raise GradingError("case_results must be a dict[str, bool]") + def _passed(case: str) -> bool: + return bool(case_results.get(case, False)) -def _report_to_grade_result( - report: dict[str, Any], - instance_id: str, -) -> GradeResult: - """Convert a ``get_eval_report`` output dict to a :class:`GradeResult`.""" - if instance_id not in report: - # Report didn't contain this instance — treat as failure. - _log.warning("Instance %s not in eval report", instance_id) - return GradeResult( - reward=0.0, - resolved=False, - patch_applied=False, - instance_id=instance_id, - ) + resolved = all(_passed(case) for case in task.FAIL_TO_PASS) and all( + _passed(case) for case in task.PASS_TO_PASS + ) - entry = report[instance_id] - resolved = bool(entry.get("resolved", False)) - patch_applied = bool(entry.get("patch_successfully_applied", False)) - tests_status = entry.get("tests_status") + tests_status = { + "FAIL_TO_PASS": { + case: "PASSED" if _passed(case) else "FAILED" + for case in task.FAIL_TO_PASS + }, + "PASS_TO_PASS": { + case: "PASSED" if _passed(case) else "FAILED" + for case in task.PASS_TO_PASS + }, + } return GradeResult( reward=1.0 if resolved else 0.0, resolved=resolved, patch_applied=patch_applied, tests_status=tests_status, - instance_id=instance_id, + instance_id=task.instance_id, ) __all__ = [ "GradeResult", "GradingError", - "grade_from_log", - "grade_from_test_output", - "make_eval_script", + "grade_from_case_results", ] diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index 595ef5ea7..de098784e 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -30,8 +30,9 @@ **Reward architecture**: The ``answer`` tool is a **host-side tool** routed through the InterceptionServer's tool routing layer (``/vf/tools``). When the agent calls ``answer()``, the request goes to the host, which -runs swebench grading (revert test files → apply test_patch → run eval → -grade via ``get_eval_report``), and returns the result to the agent. +runs SWE-Gym-native grading (revert test files → apply test_patch → run +explicit FAIL_TO_PASS/PASS_TO_PASS tests), and returns the result to the +agent. This is the same result ``verify()`` returns — one grading path, no in-sandbox grading infrastructure. @@ -48,10 +49,10 @@ import asyncio import json import logging +import shlex import time import uuid from dataclasses import dataclass, field -from pathlib import Path from typing import Any, Literal from openenv.core.harness import Message, ResourceSessionFactory, VerifyResult @@ -63,6 +64,7 @@ from openenv.core.harness.agents.interception_server import InterceptionServer from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle +from .grading import grade_from_case_results from .models import SWEGymTask, SWETask, coerce_swe_task, validate_swe_task @@ -76,6 +78,19 @@ VERIFY_TIMEOUT_S = 300 SETUP_TIMEOUT_S = 600 +_ANSWER_TOOL_DEFINITION = { + "type": "function", + "function": { + "name": "answer", + "description": "Submit your final answer for SWE grading.", + "parameters": { + "type": "object", + "properties": {}, + "additionalProperties": False, + }, + }, +} + # ── SWE instruction template ────────────────────────────────────────────── @@ -133,6 +148,7 @@ class SWEAgentConfig: model: str = "" agent_timeout_s: float = 600.0 sandbox_home: str = HOME + workdir: str = TESTBED provider: str = "" thinking: str = "off" @@ -181,6 +197,10 @@ def __init__( def swe_task(self) -> SWETask: return self._swe_task + @property + def answer_reward(self) -> float | None: + return self._answer_reward + def set_answer_reward(self, reward: float) -> None: """Called by the host-side answer tool handler to store the reward.""" self._answer_reward = reward @@ -368,7 +388,6 @@ def __init__( "interception_gate mode requires interception_base_url." ) - self._agent_name = agent self._config = config self._backend = sandbox_backend self._mode = mode @@ -413,6 +432,7 @@ def create( if episode_id else {"instance_id": swe_task.instance_id} ), + image=swe_task.sandbox_image, ) try: @@ -448,7 +468,7 @@ def create( sandbox, agent_task, self._config, base_url_override=base_url_override ) - return SWESession( + session = SWESession( swe_task=swe_task, verify_timeout_s=self._verify_timeout_s, spec=self._spec, @@ -462,6 +482,11 @@ def create( interception_queue=interception_queue, ) + if self._mode == "interception_gate": + self._register_answer_tool(session) + + return session + # ── Bootstrap helpers ────────────────────────────────────────────────── def _stage_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: @@ -512,6 +537,208 @@ def _build_agent_task(self, swe_task: SWETask) -> _SWEAgentTask: }, ) + def _register_answer_tool(self, session: SWESession) -> None: + """Register the host-side ``answer`` tool for one interception rollout.""" + + async def _answer_handler(arguments: dict[str, Any]) -> dict[str, Any]: + del arguments + + if session.answer_reward is not None: + resolved = session.answer_reward >= 1.0 + return { + "content": [ + { + "type": "text", + "text": f"✅ Resolved: {str(resolved).lower()}", + } + ] + } + + reward, resolved = await asyncio.to_thread( + self._grade_answer_submission, + session.sandbox, + session.swe_task, + ) + session.set_answer_reward(reward) + return { + "content": [ + { + "type": "text", + "text": f"✅ Resolved: {str(resolved).lower()}", + } + ] + } + + session.register_tool_handler( + "answer", + _answer_handler, + tool_definition=_ANSWER_TOOL_DEFINITION, + ) + + def _grade_answer_submission( + self, + sandbox: SandboxHandle, + swe_task: SWETask, + ) -> tuple[float, bool]: + """Compute answer-tool reward on host and return ``(reward, resolved)``.""" + try: + metadata = swe_task.metadata or {} + required = {"version", "patch", "test_patch", "FAIL_TO_PASS"} + if required.issubset(metadata): + return self._grade_with_swegym_metadata(sandbox, swe_task) + return self._grade_with_verify_commands(sandbox, swe_task) + except Exception: + _log.exception("answer-tool grading failed for %s", swe_task.instance_id) + return 0.0, False + + def _grade_with_swegym_metadata( + self, + sandbox: SandboxHandle, + swe_task: SWETask, + ) -> tuple[float, bool]: + """Grade SWE-Gym tasks directly from FAIL/PASS test-case outcomes.""" + metadata = swe_task.metadata + assert metadata is not None + + gym_task = SWEGymTask( + instance_id=swe_task.instance_id, + repo=swe_task.repo, + base_commit=swe_task.base_commit, + problem_statement=swe_task.instruction, + version=str(metadata["version"]), + patch=str(metadata["patch"]), + test_patch=str(metadata["test_patch"]), + FAIL_TO_PASS=[str(t) for t in metadata["FAIL_TO_PASS"]], + PASS_TO_PASS=[str(t) for t in metadata.get("PASS_TO_PASS", [])], + hints_text=str(metadata.get("hints_text", "")), + created_at=str(metadata.get("created_at", "")), + timeout_s=swe_task.timeout_s, + ) + + touched_files = self._extract_paths_from_test_patch(gym_task.test_patch) + self._revert_test_files( + sandbox, + base_commit=swe_task.base_commit, + paths=touched_files, + strict=True, + ) + + self._apply_test_patch(sandbox, gym_task.test_patch) + case_results = self._run_swegym_case_tests(sandbox, gym_task) + grade = grade_from_case_results(gym_task, case_results) + + # Best-effort cleanup in case grading was interrupted. + self._revert_test_files( + sandbox, + base_commit=swe_task.base_commit, + paths=touched_files, + strict=False, + ) + + return float(grade.reward), bool(grade.resolved) + + def _apply_test_patch(self, sandbox: SandboxHandle, test_patch: str) -> None: + patch_path = f"{HOME}/.openenv_swe_test_patch.diff" + sandbox.write_text(patch_path, test_patch) + result = sandbox.exec( + f"git apply --whitespace=nowarn {shlex.quote(patch_path)}", + cwd=TESTBED, + timeout=30, + ) + if result.exit_code != 0: + raise RuntimeError( + "failed to apply SWE-Gym test_patch: " + f"{(result.stderr or result.stdout or '').strip()}" + ) + + def _run_swegym_case_tests( + self, + sandbox: SandboxHandle, + gym_task: SWEGymTask, + ) -> dict[str, bool]: + cases: list[str] = [] + seen: set[str] = set() + for case in [*gym_task.FAIL_TO_PASS, *gym_task.PASS_TO_PASS]: + if case in seen: + continue + seen.add(case) + cases.append(case) + + results: dict[str, bool] = {} + for case in cases: + cmd = f"python -m pytest -q --maxfail=1 {shlex.quote(case)}" + run = sandbox.exec(cmd, cwd=TESTBED, timeout=self._verify_timeout_s) + results[case] = run.exit_code == 0 + return results + + def _grade_with_verify_commands( + self, + sandbox: SandboxHandle, + swe_task: SWETask, + ) -> tuple[float, bool]: + """Legacy fallback for non-SWE-Gym tasks.""" + if not swe_task.verify: + return 0.0, False + passed = 0 + for cmd in swe_task.verify: + r = sandbox.exec(cmd, cwd=TESTBED, timeout=self._verify_timeout_s) + if r.exit_code == 0: + passed += 1 + reward = passed / len(swe_task.verify) + return reward, reward >= 1.0 + + @staticmethod + def _extract_paths_from_test_patch(test_patch: str) -> list[str]: + paths: list[str] = [] + for line in (test_patch or "").splitlines(): + if not line.startswith("+++ b/"): + continue + path = line[len("+++ b/") :].strip() + if not path or path == "/dev/null": + continue + paths.append(path) + return sorted(set(paths)) + + def _revert_test_files( + self, + sandbox: SandboxHandle, + *, + base_commit: str, + paths: list[str], + strict: bool, + ) -> None: + if not paths: + return + + failures: list[str] = [] + for path in paths: + has_file = sandbox.exec( + f"git cat-file -e {shlex.quote(f'{base_commit}:{path}')}", + cwd=TESTBED, + timeout=10, + ) + if has_file.exit_code == 0: + cmd = ( + "git checkout --quiet " + f"{shlex.quote(base_commit)} -- {shlex.quote(path)}" + ) + else: + cmd = f"rm -f -- {shlex.quote(path)}" + + result = sandbox.exec(cmd, cwd=TESTBED, timeout=20) + if result.exit_code != 0: + failures.append( + f"{path}: {(result.stderr or result.stdout or '').strip()}" + ) + + if not failures: + return + + msg = "failed to revert test files before/after grading: " + "; ".join(failures) + if strict: + raise RuntimeError(msg) + _log.warning(msg) + __all__ = [ "SWEAgentConfig", diff --git a/envs/mini_swe_env/models.py b/envs/mini_swe_env/models.py index 188b00624..8c36e6fae 100644 --- a/envs/mini_swe_env/models.py +++ b/envs/mini_swe_env/models.py @@ -17,7 +17,6 @@ from __future__ import annotations -import json from dataclasses import asdict, dataclass, field from typing import Any @@ -77,8 +76,8 @@ def instance_image(self) -> str: def to_swe_task(self) -> SWETask: """Convert to the internal ``SWETask`` used by the harness. - The ``verify`` list is left empty because Phase 3 uses - ``swebench.harness.grading`` for reward, not shell commands. + The ``verify`` list is left empty because grading uses + SWE-Gym FAIL_TO_PASS/PASS_TO_PASS case outcomes, not shell commands. The ``instruction`` is the ``problem_statement``. """ return SWETask( @@ -89,7 +88,7 @@ def to_swe_task(self) -> SWETask: base_commit=self.base_commit, instruction=self.problem_statement, setup=[], - verify=[], # grading via swebench, not shell commands + verify=[], # grading via SWE-Gym case outcomes, not shell commands timeout_s=self.timeout_s, sandbox_image=self.instance_image, metadata={ diff --git a/envs/mini_swe_env/server/swe_environment.py b/envs/mini_swe_env/server/swe_environment.py index b7acc0385..b8ee30f69 100644 --- a/envs/mini_swe_env/server/swe_environment.py +++ b/envs/mini_swe_env/server/swe_environment.py @@ -4,7 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""SWE environment implementation (v2 — SWE-Gym + swebench grading). +"""SWE environment implementation (v2 — SWE-Gym-native grading). Single MCP tool ``run_swe_rollout`` with the ``SWEGymTask`` shape: @@ -13,7 +13,7 @@ - ``base_commit`` — commit hash in per-task Docker image - ``problem_statement`` — agent instruction (problem text) -**Reward** is binary via ``swebench.harness.grading``: +**Reward** is binary via SWE-Gym test-case outcomes: - ``1.0`` if resolved (all FAIL_TO_PASS pass, all PASS_TO_PASS still pass) - ``0.0`` otherwise @@ -29,6 +29,7 @@ import json import logging +import shlex import time from pathlib import Path from typing import Any, Optional @@ -40,7 +41,7 @@ from openenv.core.env_server.mcp_environment import MCPEnvironment from openenv.core.env_server.types import Action, Observation - from ..grading import GradeResult, grade_from_log, make_eval_script + from ..grading import GradeResult, grade_from_case_results from ..models import ( SWECommandResult, SWEGymTask, @@ -54,7 +55,7 @@ validate_swegym_task, ) except ImportError: # pragma: no cover - from grading import GradeResult, grade_from_log, make_eval_script # type: ignore + from grading import GradeResult, grade_from_case_results # type: ignore from models import ( # type: ignore SWECommandResult, SWEGymTask, @@ -77,10 +78,6 @@ # Per-task images have the repo pre-cloned at /testbed. TESTBED = "/testbed" HOME = "/home/user" -EVAL_LOG_FILE = f"{HOME}/logs/verifier/eval.log" -EVAL_SCRIPT_PATH = f"{HOME}/swe_eval.sh" -FINAL_ANSWER_FILE = f"{HOME}/logs/agent/final_answer.txt" -DONE_MARKER = f"{HOME}/logs/agent/.done" MCP_CONFIG_PATH = f"{HOME}/.swe_mcp_config.json" MCP_SERVER_PATH = f"{HOME}/.swe_mcp_server.py" MCP_PORT = 8765 @@ -102,7 +99,7 @@ class SWEEnvironment(MCPEnvironment): Updated for SWE-Gym: - Per-task Docker images (no git clone needed). - - Binary reward via ``swebench.harness.grading``. + - Binary reward via SWE-Gym case outcomes. - Working directory is ``/testbed``. """ @@ -474,16 +471,11 @@ def _grade_submission( sandbox: Any, task: SWETask, ) -> GradeResult | None: - """Run swebench grading if task metadata contains SWE-Gym fields. - - Returns GradeResult on success, None if task lacks metadata for - swebench grading (falls back to legacy verify). - """ + """Run SWE-Gym-native grading when metadata includes test lists.""" metadata = task.metadata if not metadata: return None - # Need SWE-Gym fields for proper grading. required_keys = {"patch", "test_patch", "FAIL_TO_PASS", "version"} if not required_keys.issubset(metadata.keys()): return None @@ -507,42 +499,107 @@ def _grade_submission( _log.warning("Could not reconstruct SWEGymTask for grading: %s", exc) return None + touched_files = self._extract_paths_from_test_patch(gym_task.test_patch) try: - # Deploy eval script and run it. - eval_script = make_eval_script(gym_task) - sandbox.write_text(EVAL_SCRIPT_PATH, eval_script) - sandbox.exec(f"chmod +x {EVAL_SCRIPT_PATH}", timeout=5) + self._revert_test_files( + sandbox, + base_commit=task.base_commit, + paths=touched_files, + strict=True, + ) + self._apply_test_patch(sandbox, gym_task.test_patch) + case_results = self._run_swegym_case_tests(sandbox, gym_task) + return grade_from_case_results(gym_task, case_results) + except Exception as exc: + _log.warning("SWE-Gym grading failed, falling back: %s", exc) + return None + finally: + self._revert_test_files( + sandbox, + base_commit=task.base_commit, + paths=touched_files, + strict=False, + ) + def _apply_test_patch(self, sandbox: Any, test_patch: str) -> None: + patch_path = f"{HOME}/.openenv_swe_test_patch.diff" + sandbox.write_text(patch_path, test_patch) + r = sandbox.exec( + f"git apply --whitespace=nowarn {shlex.quote(patch_path)}", + cwd=TESTBED, + timeout=30, + ) + if r.exit_code != 0: + raise RuntimeError((r.stderr or r.stdout or "").strip()) + + def _run_swegym_case_tests( + self, + sandbox: Any, + task: SWEGymTask, + ) -> dict[str, bool]: + cases: list[str] = [] + seen: set[str] = set() + for case in [*task.FAIL_TO_PASS, *task.PASS_TO_PASS]: + if case in seen: + continue + seen.add(case) + cases.append(case) + + results: dict[str, bool] = {} + for case in cases: r = sandbox.exec( - f"bash {EVAL_SCRIPT_PATH} 2>&1 | tee {EVAL_LOG_FILE}", + f"python -m pytest -q --maxfail=1 {shlex.quote(case)}", cwd=TESTBED, timeout=VERIFY_TIMEOUT_S, ) + results[case] = r.exit_code == 0 + return results - # Grade from log file. - # We need the log file on the host, so read it from sandbox. - log_content = sandbox.read_text(EVAL_LOG_FILE) - - import tempfile - - with tempfile.NamedTemporaryFile( - mode="w", suffix=".log", delete=False, prefix="swe_grade_" - ) as f: - f.write(log_content) - tmp_log = f.name - - try: - result = grade_from_log(gym_task, tmp_log) - finally: - import os - - os.unlink(tmp_log) + @staticmethod + def _extract_paths_from_test_patch(test_patch: str) -> list[str]: + paths: list[str] = [] + for line in (test_patch or "").splitlines(): + if not line.startswith("+++ b/"): + continue + path = line[len("+++ b/") :].strip() + if not path or path == "/dev/null": + continue + paths.append(path) + return sorted(set(paths)) - return result + def _revert_test_files( + self, + sandbox: Any, + *, + base_commit: str, + paths: list[str], + strict: bool, + ) -> None: + if not paths: + return + + failures: list[str] = [] + for path in paths: + has_file = sandbox.exec( + f"git cat-file -e {shlex.quote(f'{base_commit}:{path}')}", + cwd=TESTBED, + timeout=10, + ) + if has_file.exit_code == 0: + cmd = ( + "git checkout --quiet " + f"{shlex.quote(base_commit)} -- {shlex.quote(path)}" + ) + else: + cmd = f"rm -f -- {shlex.quote(path)}" + result = sandbox.exec(cmd, cwd=TESTBED, timeout=20) + if result.exit_code != 0: + failures.append( + f"{path}: {(result.stderr or result.stdout or '').strip()}" + ) - except Exception as exc: - _log.warning("swebench grading failed, falling back: %s", exc) - return None + if failures and strict: + raise RuntimeError("; ".join(failures)) def _legacy_verify( self, diff --git a/examples/mini_swe_env/README.md b/examples/mini_swe_env/README.md new file mode 100644 index 000000000..b35e9524f --- /dev/null +++ b/examples/mini_swe_env/README.md @@ -0,0 +1,177 @@ +# SWE-Gym Training Example + +End-to-end training pipeline: SWE-Gym tasks → Pi agent → InterceptionServer → GRPO. + +## Overview + +This example demonstrates how to train a coding agent on real-world +software engineering tasks from [SWE-Gym](https://huggingface.co/datasets/SWE-Gym/SWE-Gym) +using the OpenEnv harness infrastructure. + +### Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ train_swe_grpo.py │ +│ │ +│ 1. Load SWE-Gym tasks from HuggingFace │ +│ 2. Start InterceptionServer on trainer host │ +│ 3. For each batch: │ +│ a. SWESessionFactory.create(task) → sandbox + Pi │ +│ b. Pi makes LLM call → intercepted by server │ +│ c. Training loop runs vLLM forward, captures logprobs │ +│ d. Delivers response back to Pi │ +│ e. Repeat until Pi exits or calls answer() │ +│ f. session.verify() → host-side SWE-Gym grading │ +│ 4. Compute GRPO advantages and update policy │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Reward Integrity + +The training reward is computed **host-side** by `SWESession.verify()`: + +1. Reverts any test files the agent modified (anti-reward-hacking) +2. Applies the known-good `test_patch` +3. Runs the explicit `FAIL_TO_PASS` and `PASS_TO_PASS` pytest cases +4. Computes binary reward from those case outcomes +5. Returns binary reward: `1.0` (resolved) or `0.0` (not resolved) + +The agent **cannot** influence the training reward. The in-sandbox +`answer` tool only gives the agent a feedback signal ("Resolved: true/false"). + +## Files + +| File | Purpose | +|------|---------| +| `config.py` | All training knobs (`SWETrainingConfig`) | +| `smoke_swe.py` | Single-task smoke test (3 modes) | +| `run_swe_sample.py` | Real end-to-end interception rollout with a live LLM | +| `train_swe_grpo.py` | GRPO training loop (reference impl) | + +## Quick Start + +### 1. Dry Run (no sandbox needed) + +Validates config and task loading: + +```bash +PYTHONPATH=src:envs python examples/mini_swe_env/smoke_swe.py --dry-run +``` + +### 2. Smoke Test — Interception Gate (no LLM needed) + +Spins up InterceptionServer, creates a real sandbox, and auto-responds +to prove the full pipeline works: + +```bash +PYTHONPATH=src:envs python examples/mini_swe_env/smoke_swe.py --interception +``` + +Requires Docker and the per-task SWE-Gym image to be pullable. + +### 3. Smoke Test — Black Box (requires LLM endpoint) + +Pi talks directly to an LLM endpoint: + +```bash +SWE_BASE_URL=http://localhost:8000/v1 \ +SWE_API_KEY=test \ +SWE_MODEL=qwen3-8b \ +PYTHONPATH=src:envs python examples/mini_swe_env/smoke_swe.py +``` + +### 4. Real end-to-end run (live LLM) + +```bash +SWE_LLM_BASE_URL=https://api.openai.com/v1 \ +SWE_LLM_API_KEY=... \ +SWE_LLM_MODEL=gpt-4o-mini \ +PYTHONPATH=src:envs python examples/mini_swe_env/run_swe_sample.py \ + --task-variant lite --task-index 0 --assert-host-answer +``` + +Notes: +- Uses `interception_gate` mode and forwards every intercepted request to the + configured OpenAI-compatible endpoint. +- If `--assert-host-answer` is set, the script fails unless reward comes from + the host-side `answer` tool path (`reward_source=host_answer_tool`). + +### 5. GRPO Training (reference) + +```bash +PYTHONPATH=src:envs python examples/mini_swe_env/train_swe_grpo.py --smoke +``` + +For full training, adjust `config.py` or use environment variables: + +```bash +SWE_TASK_VARIANT=lite \ +SWE_MODEL=Qwen/Qwen3-32B \ +SWE_GRPO_BATCH_SIZE=4 \ +SWE_SANDBOX_BACKEND=docker \ +PYTHONPATH=src:envs python examples/mini_swe_env/train_swe_grpo.py +``` + +## Configuration + +All settings are in `config.py` (`SWETrainingConfig`). Override via +`SWE_*` environment variables: + +| Env Variable | Default | Description | +|-------------|---------|-------------| +| `SWE_TASK_VARIANT` | `lite` | `"lite"` (230) or `"full"` (2,438 tasks) | +| `SWE_MODEL` | `Qwen/Qwen3-8B` | HuggingFace model id | +| `SWE_BASE_URL` | *(empty)* | LLM endpoint (black-box mode) | +| `SWE_API_KEY` | *(empty)* | LLM bearer token | +| `SWE_SANDBOX_BACKEND` | `docker` | `"docker"`, `"e2b"`, or `"hf"` | +| `SWE_INTERCEPTION_PORT` | `9090` | InterceptionServer port | +| `SWE_INTERCEPTION_BASE_URL` | auto | URL reachable from sandbox | +| `SWE_AGENT_TIMEOUT` | `1800` | Agent timeout (seconds) | +| `SWE_MAX_TURNS` | `30` | Max agent turns per rollout | +| `SWE_GRPO_BATCH_SIZE` | `4` | Tasks per GRPO batch | +| `SWE_GRPO_LR` | `1e-6` | Learning rate | +| `SWE_GRPO_BETA` | `0.04` | KL penalty | +| `SWE_MAX_TASKS` | *(all)* | Cap tasks for debugging | + +## Sandbox Connectivity + +The InterceptionServer runs on the trainer host. The sandbox must be +able to reach it: + +- **Docker (same host)**: Automatic — uses `http://host.docker.internal:` +- **Remote sandbox (E2B, HF)**: You must provide a tunnel URL via + `SWE_INTERCEPTION_BASE_URL`. Options: [bore](https://github.com/ekzhang/bore), + [ngrok](https://ngrok.com/), [frp](https://github.com/fatedier/frp), + or a public IP. + +## Task Source + +Tasks are loaded at runtime from HuggingFace: + +- **Lite** (230 tasks): `SWE-Gym/SWE-Gym-Lite` — good for development +- **Full** (2,438 tasks): `SWE-Gym/SWE-Gym` — for production training + +Each task comes with a per-task Docker image (`xingyaoww/sweb.eval.x86_64.*`) +that has the repo pre-cloned at `/testbed` with all dependencies installed. + +## What the Model Learns + +During training, the model sees Pi's system prompt, tool calls, and +results — the exact format it will use at inference time: + +``` +[user] + +[assistant] Let me investigate... +[tool_call] bash(command="cd /testbed && python -m pytest tests/test_foo.py -x -q") +[tool_result] FAILED tests/test_foo.py::test_bar - AssertionError... + +[assistant] I see the bug. +[tool_call] edit(path="/testbed/src/foo.py", edits=[{oldText: "if x > 0", newText: "if x >= 0"}]) +[tool_result] ✅ Applied 1 edit + +[assistant] Fix verified. Submitting. +[tool_call] answer() +[tool_result] ✅ Resolved: true +``` diff --git a/examples/mini_swe_env/run_swe_sample.py b/examples/mini_swe_env/run_swe_sample.py new file mode 100644 index 000000000..8199b147f --- /dev/null +++ b/examples/mini_swe_env/run_swe_sample.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +"""Run one real SWE-Gym interception rollout against an actual LLM. + +This is a true end-to-end harness test: + + SWE-Gym task -> SWESessionFactory -> Pi in sandbox + -> InterceptionServer -> real OpenAI-compatible LLM + -> host-side answer tool grading -> session.verify() + +Required env vars: + SWE_LLM_BASE_URL e.g. https://api.openai.com/v1 + SWE_LLM_API_KEY bearer token + SWE_LLM_MODEL e.g. gpt-4o-mini + +Example: + SWE_LLM_BASE_URL=https://api.openai.com/v1 \ + SWE_LLM_API_KEY=... \ + SWE_LLM_MODEL=gpt-4o-mini \ + PYTHONPATH=src:envs python examples/mini_swe_env/run_swe_sample.py +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import logging +import os +import sys +import time +import uuid +from pathlib import Path + +import httpx + +_root = Path(__file__).resolve().parent.parent.parent +for _p in (_root / "src", _root / "envs"): + if str(_p) not in sys.path: + sys.path.insert(0, str(_p)) + +from mini_swe_env.harness import SWEAgentConfig, SWESessionFactory +from mini_swe_env.task_loader_swegym import load_swegym_tasks +from openenv.core.harness.agents.interception_server import InterceptionServer +from openenv.core.harness.sandbox import create_sandbox_backend + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(name)s %(levelname)s %(message)s", + datefmt="%H:%M:%S", +) +_log = logging.getLogger("swe-e2e") + + +def _arg_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser(description="Run one real SWE interception rollout") + p.add_argument("--task-variant", default="lite", choices=["lite", "full"]) + p.add_argument("--task-index", type=int, default=0) + p.add_argument("--sandbox-backend", default="docker", choices=["docker", "e2b", "hf"]) + p.add_argument("--interception-port", type=int, default=9090) + p.add_argument("--interception-host", default="0.0.0.0") + p.add_argument("--interception-base-url", default="") + p.add_argument("--agent-timeout-s", type=float, default=1800.0) + p.add_argument("--request-timeout-s", type=float, default=180.0) + p.add_argument("--max-turns", type=int, default=50) + p.add_argument("--assert-host-answer", action="store_true") + return p + + +def _must_env(name: str) -> str: + value = os.environ.get(name, "").strip() + if not value: + raise RuntimeError(f"Missing required env var: {name}") + return value + + +async def _forward_to_llm( + client: httpx.AsyncClient, + *, + intercept: dict, + base_url: str, + api_key: str, + model: str, + timeout_s: float, +) -> dict: + body = dict(intercept.get("body") or {}) + body["model"] = model + body["logprobs"] = True + body["top_logprobs"] = 5 + body.pop("stream", None) + body.pop("stream_options", None) + + r = await client.post( + f"{base_url.rstrip('/')}/chat/completions", + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json=body, + timeout=timeout_s, + ) + if r.status_code != 200: + raise RuntimeError(f"LLM error {r.status_code}: {r.text[:500]}") + return r.json() + + +async def _invoke_answer_tool( + client: httpx.AsyncClient, + *, + server_port: int, + secret: str, + rollout_id: str, +) -> dict: + r = await client.post( + f"http://127.0.0.1:{server_port}/rollout/{rollout_id}/v1/tools/answer", + headers={"Authorization": f"Bearer {secret}"}, + json={"arguments": {}}, + timeout=180.0, + ) + if r.status_code != 200: + raise RuntimeError(f"answer tool error {r.status_code}: {r.text[:500]}") + return r.json() + + +async def _run(args: argparse.Namespace) -> int: + llm_base_url = _must_env("SWE_LLM_BASE_URL") + llm_api_key = _must_env("SWE_LLM_API_KEY") + llm_model = _must_env("SWE_LLM_MODEL") + + tasks = load_swegym_tasks(args.task_variant) + if not (0 <= args.task_index < len(tasks)): + raise RuntimeError( + f"task-index {args.task_index} out of range [0, {len(tasks) - 1}]" + ) + gym_task = tasks[args.task_index] + swe_task = gym_task.to_swe_task() + + server = InterceptionServer(port=args.interception_port, host=args.interception_host) + await server.start() + + try: + interception_base_url = args.interception_base_url.strip() + if not interception_base_url: + if args.sandbox_backend != "docker": + raise RuntimeError( + "For non-docker backends, pass --interception-base-url " + "that sandboxes can reach (tunnel/public URL)." + ) + interception_base_url = f"http://host.docker.internal:{server.port}" + + _log.info("Task: %s (%s)", gym_task.instance_id, gym_task.repo) + _log.info("Sandbox image: %s", gym_task.instance_image) + _log.info("Interception server: %s", interception_base_url) + + backend = create_sandbox_backend(args.sandbox_backend) + cfg = SWEAgentConfig( + base_url=interception_base_url, + api_key=server.secret, + model=llm_model, + agent_timeout_s=args.agent_timeout_s, + ) + factory = SWESessionFactory( + agent="pi", + config=cfg, + sandbox_backend=backend, + mode="interception_gate", + interception_server=server, + interception_base_url=interception_base_url, + ) + + episode_id = f"swe-e2e-{uuid.uuid4().hex[:8]}" + t0 = time.time() + session = factory.create(task=swe_task, episode_id=episode_id) + _log.info("Session created in %.1fs", time.time() - t0) + + turns = 0 + answer_requested = False + answer_bridge_invoked = False + logprob_tokens = 0 + + try: + async with httpx.AsyncClient() as client: + while turns < args.max_turns: + intercept = await session.next_request(timeout_s=args.agent_timeout_s) + if intercept is None: + _log.info("Agent exited") + break + + turns += 1 + llm_resp = await _forward_to_llm( + client, + intercept=intercept, + base_url=llm_base_url, + api_key=llm_api_key, + model=llm_model, + timeout_s=args.request_timeout_s, + ) + + choice0 = (llm_resp.get("choices") or [{}])[0] + msg = choice0.get("message") or {} + tool_calls = msg.get("tool_calls") or [] + if any( + (tc.get("function") or {}).get("name") == "answer" + for tc in tool_calls + ): + answer_requested = True + answer_resp = await _invoke_answer_tool( + client, + server_port=server.port, + secret=server.secret, + rollout_id=str(intercept.get("rollout_id", "")), + ) + answer_bridge_invoked = True + _log.info("host answer bridge response: %s", json.dumps(answer_resp, default=str)) + + # Replace tool-call response with plain assistant text so + # Pi doesn't need native /v1/tools client support. + llm_resp = dict(llm_resp) + choices = list(llm_resp.get("choices") or []) + if choices: + choice0 = dict(choices[0]) + msg0 = dict(choice0.get("message") or {}) + msg0.pop("tool_calls", None) + msg0["content"] = ( + (msg0.get("content") or "") + + "\nSubmission received and graded on host." + ).strip() + choice0["message"] = msg0 + choice0["finish_reason"] = "stop" + choices[0] = choice0 + llm_resp["choices"] = choices + + lp = (choice0.get("logprobs") or {}).get("content") or [] + logprob_tokens += len(lp) + + _log.info( + "turn=%d finish=%s tools=%s lp_tokens=%d", + turns, + choice0.get("finish_reason"), + [ + (tc.get("function") or {}).get("name", "?") + for tc in tool_calls + ], + len(lp), + ) + + await session.deliver(intercept, llm_resp) + + if answer_bridge_invoked: + break + + vr = session.verify(transcript=[]) + _log.info("verify reward=%s metrics=%s", vr.env_reward, json.dumps(vr.metrics, default=str)) + _log.info("turns=%d logprob_tokens=%d", turns, logprob_tokens) + + if turns == 0: + try: + artifacts = session.collect_artifacts() + agent_log = artifacts.get("agent_log", "") if isinstance(artifacts, dict) else "" + if isinstance(agent_log, str) and agent_log.strip(): + _log.warning("agent_log tail:\n%s", agent_log[-2000:]) + except Exception as exc: + _log.warning("Failed to collect agent log: %s", exc) + + if args.assert_host_answer: + src = (vr.metrics or {}).get("reward_source") + if src != "host_answer_tool": + raise RuntimeError( + "Expected reward_source=host_answer_tool, got " + f"{src!r}; model may not have called answer()." + ) + + print("\n" + "=" * 68) + print(f"Task : {gym_task.instance_id}") + print(f"Turns : {turns}") + print(f"Logprob tokens : {logprob_tokens}") + print(f"Answer called : {answer_requested}") + print(f"Answer bridged : {answer_bridge_invoked}") + print(f"Reward : {vr.env_reward}") + print(f"Reward source : {(vr.metrics or {}).get('reward_source')}") + print("=" * 68) + return 0 + finally: + session.close() + finally: + await server.stop() + + +def main() -> None: + args = _arg_parser().parse_args() + raise SystemExit(asyncio.run(_run(args))) + + +if __name__ == "__main__": + main() From b6e295e91f42c3fb3e80135ca48e11fca9986da6 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 14:12:27 +0530 Subject: [PATCH 30/79] feat: update default task index to known easy task --- examples/mini_swe_env/README.md | 2 +- examples/mini_swe_env/run_swe_sample.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/mini_swe_env/README.md b/examples/mini_swe_env/README.md index b35e9524f..6b5a3c39f 100644 --- a/examples/mini_swe_env/README.md +++ b/examples/mini_swe_env/README.md @@ -88,7 +88,7 @@ SWE_LLM_BASE_URL=https://api.openai.com/v1 \ SWE_LLM_API_KEY=... \ SWE_LLM_MODEL=gpt-4o-mini \ PYTHONPATH=src:envs python examples/mini_swe_env/run_swe_sample.py \ - --task-variant lite --task-index 0 --assert-host-answer + --task-variant lite --task-index 16 --assert-host-answer ``` Notes: diff --git a/examples/mini_swe_env/run_swe_sample.py b/examples/mini_swe_env/run_swe_sample.py index 8199b147f..e9cd10d52 100644 --- a/examples/mini_swe_env/run_swe_sample.py +++ b/examples/mini_swe_env/run_swe_sample.py @@ -50,11 +50,20 @@ ) _log = logging.getLogger("swe-e2e") +# Known easy SWE-Gym Lite task that has repeatedly produced reward=1.0 +# in local end-to-end validation with qwen-3.6-27b. +DEFAULT_TASK_INDEX = 16 # getmoto__moto-5699 + def _arg_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser(description="Run one real SWE interception rollout") p.add_argument("--task-variant", default="lite", choices=["lite", "full"]) - p.add_argument("--task-index", type=int, default=0) + p.add_argument( + "--task-index", + type=int, + default=DEFAULT_TASK_INDEX, + help=f"Task index in variant split (default: {DEFAULT_TASK_INDEX}, getmoto__moto-5699)", + ) p.add_argument("--sandbox-backend", default="docker", choices=["docker", "e2b", "hf"]) p.add_argument("--interception-port", type=int, default=9090) p.add_argument("--interception-host", default="0.0.0.0") From 42fd066d8e77ee4bb28777b7af827c075cf6e635 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 14:25:38 +0530 Subject: [PATCH 31/79] feat: impl control-plane primitives and entrypoint --- envs/mini_swe_env/async_grpo/__init__.py | 19 ++ envs/mini_swe_env/async_grpo/control_plane.py | 168 ++++++++++++++++++ .../async_grpo/interception_app.py | 54 ++++++ envs/mini_swe_env/harness.py | 10 +- envs/mini_swe_env/server/swe_environment.py | 8 +- src/openenv/core/harness/agents/cli_driver.py | 18 +- .../harness/agents/interception_server.py | 29 ++- .../core/harness/sandbox/hf_backend.py | 5 +- 8 files changed, 296 insertions(+), 15 deletions(-) create mode 100644 envs/mini_swe_env/async_grpo/__init__.py create mode 100644 envs/mini_swe_env/async_grpo/control_plane.py create mode 100644 envs/mini_swe_env/async_grpo/interception_app.py diff --git a/envs/mini_swe_env/async_grpo/__init__.py b/envs/mini_swe_env/async_grpo/__init__.py new file mode 100644 index 000000000..5b68e767a --- /dev/null +++ b/envs/mini_swe_env/async_grpo/__init__.py @@ -0,0 +1,19 @@ +"""Async GRPO control-plane primitives for Mini SWE on HF Spaces. + +This package provides control-plane setup utilities: +- configure + launch the interception server on Space-exposed networking, +- keep trainer-facing rollout registration in the same process, +- expose simple runtime stats for leak detection. +""" + +from .control_plane import ( + SWEAsyncControlPlane, + SWEAsyncControlPlaneConfig, + build_hf_space_base_url, +) + +__all__ = [ + "SWEAsyncControlPlane", + "SWEAsyncControlPlaneConfig", + "build_hf_space_base_url", +] diff --git a/envs/mini_swe_env/async_grpo/control_plane.py b/envs/mini_swe_env/async_grpo/control_plane.py new file mode 100644 index 000000000..f6bd64a2d --- /dev/null +++ b/envs/mini_swe_env/async_grpo/control_plane.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +import asyncio +import contextlib +import logging +import os +from dataclasses import dataclass +from typing import Any, Mapping + +from openenv.core.harness.agents.interception_server import InterceptionServer + + +_log = logging.getLogger(__name__) + +_DEFAULT_INTERCEPTION_HOST = "0.0.0.0" +_DEFAULT_INTERCEPTION_PORT = 8765 + + +def build_hf_space_base_url(space_id: str) -> str: + """Return canonical public Space URL from ``owner/name``.""" + owner, sep, name = (space_id or "").strip().partition("/") + if not sep or not owner or not name: + raise ValueError( + "SPACE_ID must be in 'owner/name' format to derive interception base URL" + ) + return f"https://{owner}-{name}.hf.space" + + +@dataclass(frozen=True) +class SWEAsyncControlPlaneConfig: + """Runtime config for the Space-hosted interception control plane.""" + + host: str + port: int + auth_token: str + interception_base_url: str + + @classmethod + def from_env( + cls, + *, + env: Mapping[str, str] | None = None, + require_auth_token: bool = True, + ) -> "SWEAsyncControlPlaneConfig": + source = env or os.environ + + host = source.get("INTERCEPTION_HOST", _DEFAULT_INTERCEPTION_HOST).strip() + port_raw = source.get("INTERCEPTION_PORT") or source.get("PORT") + port = int(port_raw) if port_raw else _DEFAULT_INTERCEPTION_PORT + + token = ( + source.get("INTERCEPTION_AUTH_TOKEN") + or source.get("INTERCEPTION_SECRET") + or "" + ).strip() + if require_auth_token and not token: + raise ValueError( + "Missing interception auth token. Set INTERCEPTION_AUTH_TOKEN in Space secrets." + ) + + base_url = (source.get("INTERCEPTION_BASE_URL") or "").strip().rstrip("/") + if not base_url: + space_host = (source.get("SPACE_HOST") or "").strip().rstrip("/") + if space_host: + if "http" not in space_host: + space_host = f"https://{space_host}" + base_url = space_host.rstrip("/") + else: + space_id = (source.get("SPACE_ID") or "").strip() + if space_id: + base_url = build_hf_space_base_url(space_id) + + if not base_url: + raise ValueError( + "Could not resolve interception_base_url. Set INTERCEPTION_BASE_URL, " + "SPACE_HOST, or SPACE_ID." + ) + + return cls( + host=host, + port=port, + auth_token=token, + interception_base_url=base_url, + ) + + +class SWEAsyncControlPlane: + """Owns the interception server and rollout registration state. + + Trainer-side async rollout workers should use this object directly for + ``register_rollout/get_intercept/unregister_rollout`` so rollout queues and + intercept state remain in-process with the trainer. + """ + + def __init__( + self, + *, + config: SWEAsyncControlPlaneConfig, + server: InterceptionServer | None = None, + ) -> None: + self.config = config + self.server = server or InterceptionServer( + host=config.host, + port=config.port, + secret=config.auth_token, + ) + + @property + def interception_base_url(self) -> str: + return self.config.interception_base_url + + @property + def auth_token(self) -> str: + return self.config.auth_token + + async def start(self) -> None: + await self.server.start() + # If port was 0 in config, InterceptionServer resolves it after bind. + _log.info( + "swe_async_control_plane_started base_url=%s port=%d", + self.interception_base_url, + self.server.port, + ) + + async def stop(self) -> None: + await self.server.stop() + _log.info("swe_async_control_plane_stopped") + + def register_rollout( + self, + rollout_id: str, + *, + state: dict[str, Any] | None = None, + ) -> asyncio.Queue: + queue = self.server.register_rollout(rollout_id, state=state) + stats = self.stats() + _log.info( + "swe_async_rollout_registered rollout_id=%s active_rollouts=%d", + rollout_id, + stats["active_rollouts"], + ) + return queue + + def unregister_rollout(self, rollout_id: str) -> None: + self.server.unregister_rollout(rollout_id) + stats = self.stats() + _log.info( + "swe_async_rollout_unregistered rollout_id=%s active_rollouts=%d", + rollout_id, + stats["active_rollouts"], + ) + + @contextlib.contextmanager + def rollout( + self, + rollout_id: str, + *, + state: dict[str, Any] | None = None, + ): + """Leak-safe context manager for one rollout registration.""" + queue = self.register_rollout(rollout_id, state=state) + try: + yield queue + finally: + self.unregister_rollout(rollout_id) + + def stats(self) -> dict[str, int]: + return self.server.stats() diff --git a/envs/mini_swe_env/async_grpo/interception_app.py b/envs/mini_swe_env/async_grpo/interception_app.py new file mode 100644 index 000000000..7bae5216e --- /dev/null +++ b/envs/mini_swe_env/async_grpo/interception_app.py @@ -0,0 +1,54 @@ +"""Entrypoint for Space-hosted interception control plane. + +Run inside the HF Space process that should expose interception endpoints: + + PYTHONPATH=src:envs python -m mini_swe_env.async_grpo.interception_app +""" + +from __future__ import annotations + +import asyncio +import logging +import signal + +from .control_plane import SWEAsyncControlPlane, SWEAsyncControlPlaneConfig + + +_log = logging.getLogger(__name__) + + +async def run_forever(config: SWEAsyncControlPlaneConfig | None = None) -> None: + cfg = config or SWEAsyncControlPlaneConfig.from_env() + plane = SWEAsyncControlPlane(config=cfg) + await plane.start() + + stop_event = asyncio.Event() + + def _request_stop() -> None: + if not stop_event.is_set(): + _log.info("swe_async_control_plane_shutdown_requested") + stop_event.set() + + loop = asyncio.get_running_loop() + for sig in (signal.SIGINT, signal.SIGTERM): + try: + loop.add_signal_handler(sig, _request_stop) + except NotImplementedError: + pass + + try: + await stop_event.wait() + finally: + await plane.stop() + + +def main() -> None: + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s %(name)s %(message)s", + ) + asyncio.run(run_forever()) + + +if __name__ == "__main__": + main() diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index de098784e..c85ea6f0c 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -60,6 +60,7 @@ from openenv.core.harness.agents.cli_driver import ( CLIAgentDriver, CLIAgentSession, + build_interception_rollout_url, ) from openenv.core.harness.agents.interception_server import InterceptionServer from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle @@ -437,7 +438,7 @@ def create( try: if not swe_task.sandbox_image: - self._stage_repo(sandbox, swe_task) + self._prepare_repo(sandbox, swe_task) self._run_setup(sandbox, swe_task) @@ -459,8 +460,9 @@ def create( rollout_id = episode_id or f"rollout_{uuid.uuid4().hex[:8]}" interception_rollout_id = rollout_id interception_queue = self._interception_server.register_rollout(rollout_id) - base_url_override = ( - f"{self._interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1" + base_url_override = build_interception_rollout_url( + self._interception_base_url, + rollout_id, ) agent_task = self._build_agent_task(swe_task) @@ -489,7 +491,7 @@ def create( # ── Bootstrap helpers ────────────────────────────────────────────────── - def _stage_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: + def _prepare_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: """Clone the repo and reset to base_commit.""" sandbox.exec(f"mkdir -p {TESTBED}", timeout=10) clone_url = f"https://github.com/{task.repo}.git" diff --git a/envs/mini_swe_env/server/swe_environment.py b/envs/mini_swe_env/server/swe_environment.py index b8ee30f69..916d6f938 100644 --- a/envs/mini_swe_env/server/swe_environment.py +++ b/envs/mini_swe_env/server/swe_environment.py @@ -4,7 +4,7 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""SWE environment implementation (v2 — SWE-Gym-native grading). +"""SWE environment implementation with SWE-Gym-native grading. Single MCP tool ``run_swe_rollout`` with the ``SWEGymTask`` shape: @@ -298,9 +298,9 @@ def _run_swe_rollout_impl( ) result.sandbox_id = sandbox.sandbox_id - # ── Stage repo (only if not using a per-task image) ─────── + # ── Prepare repo (only if not using a per-task image) ───── if not image: - self._stage_repo(sandbox, task) + self._prepare_repo(sandbox, task) # ── Run setup commands ──────────────────────────────────── for cmd in task.setup: @@ -630,7 +630,7 @@ def _create_backend(self, backend_name: str, image: str | None) -> Any: kwargs["image"] = image return create_sandbox_backend(backend_name, **kwargs) - def _stage_repo(self, sandbox: Any, task: SWETask) -> None: + def _prepare_repo(self, sandbox: Any, task: SWETask) -> None: """Clone the repo and reset to base_commit in the sandbox. Only used when there is no per-task Docker image. SWE-Gym diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 2ff07e4d2..cb2ec3c77 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -48,6 +48,11 @@ Verifier = Callable[..., VerifyResult] +def build_interception_rollout_url(base_url: str, rollout_id: str) -> str: + """Build OpenAI-compatible interception endpoint for one rollout.""" + return f"{base_url.rstrip('/')}/rollout/{rollout_id}/v1" + + class _ConfigOverrideView: """Read-only attribute view with optional overrides.""" @@ -325,8 +330,9 @@ def create_session( rollout_id = episode_id or f"rollout_{uuid.uuid4().hex[:8]}" interception_rollout_id = rollout_id interception_queue = self._interception_server.register_rollout(rollout_id) - base_url_override = ( - f"{self._interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1" + base_url_override = build_interception_rollout_url( + self._interception_base_url, + rollout_id, ) agent_bg_job = self._start_agent( @@ -631,4 +637,10 @@ def create( ) -__all__ = ["CLIAgentDriver", "CLIAgentSession", "CLIAgentSessionFactory", "Verifier"] +__all__ = [ + "CLIAgentDriver", + "CLIAgentSession", + "CLIAgentSessionFactory", + "Verifier", + "build_interception_rollout_url", +] diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index 70f8c4247..0dc1111ba 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -164,6 +164,12 @@ def register_rollout( "tool_handlers": {}, "tool_defs": {}, } + active = len(self.active_rollouts) + _log.info( + "interception_rollout_registered rollout_id=%s active_rollouts=%d", + rollout_id, + active, + ) return queue def unregister_rollout(self, rollout_id: str) -> None: @@ -176,7 +182,9 @@ def unregister_rollout(self, rollout_id: str) -> None: matching_intercepts = [self.intercepts[i] for i in matching_ids] for request_id in matching_ids: del self.intercepts[request_id] - self.active_rollouts.pop(rollout_id, None) + removed = self.active_rollouts.pop(rollout_id, None) is not None + active = len(self.active_rollouts) + pending = len(self.intercepts) for intercept in matching_intercepts: fut: asyncio.Future | None = intercept.get("response_future") @@ -189,10 +197,27 @@ def unregister_rollout(self, rollout_id: str) -> None: except asyncio.QueueFull: pass + _log.info( + "interception_rollout_unregistered rollout_id=%s removed=%s " + "active_rollouts=%d pending_intercepts=%d", + rollout_id, + removed, + active, + pending, + ) + def get_intercept(self, request_id: str) -> dict[str, Any] | None: with self._state_lock: return self.intercepts.get(request_id) + def stats(self) -> dict[str, int]: + """Return lightweight runtime counters for health/debug views.""" + with self._state_lock: + return { + "active_rollouts": len(self.active_rollouts), + "pending_intercepts": len(self.intercepts), + } + def register_tool_handler( self, rollout_id: str, @@ -269,7 +294,7 @@ def _authorized(self, request: web.Request) -> bool: ) or hmac.compare_digest(api_key, self.secret) async def _handle_health(self, request: web.Request) -> web.Response: - return web.json_response({"status": "ok"}) + return web.json_response({"status": "ok", **self.stats()}) async def _handle_tool_call(self, request: web.Request) -> web.Response: if not self._authorized(request): diff --git a/src/openenv/core/harness/sandbox/hf_backend.py b/src/openenv/core/harness/sandbox/hf_backend.py index 3857ea7e6..3b7b060b5 100644 --- a/src/openenv/core/harness/sandbox/hf_backend.py +++ b/src/openenv/core/harness/sandbox/hf_backend.py @@ -231,15 +231,16 @@ def create( image: str | None = None, ) -> SandboxHandle: # `hf-sandbox` does not support metadata at create-time yet. - del metadata, image + del metadata timeout = self._timeout or _format_timeout(timeout_s) + effective_image = image or self._image last_error: Exception | None = None for attempt in range(self._create_retries): try: sbx = Sandbox.create( - image=self._image, + image=effective_image, flavor=self._flavor, timeout=timeout, forward_hf_token=self._forward_hf_token, From 66026e8b60aa05db4fbfd79af0c8c712a0c02a16 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 17:26:26 +0530 Subject: [PATCH 32/79] feat: async rollout processing --- envs/mini_swe_env/async_grpo/__init__.py | 12 + .../mini_swe_env/async_grpo/rollout_worker.py | 448 ++++++++++++++++++ 2 files changed, 460 insertions(+) create mode 100644 envs/mini_swe_env/async_grpo/rollout_worker.py diff --git a/envs/mini_swe_env/async_grpo/__init__.py b/envs/mini_swe_env/async_grpo/__init__.py index 5b68e767a..05122131d 100644 --- a/envs/mini_swe_env/async_grpo/__init__.py +++ b/envs/mini_swe_env/async_grpo/__init__.py @@ -11,9 +11,21 @@ SWEAsyncControlPlaneConfig, build_hf_space_base_url, ) +from .rollout_worker import ( + SWEAsyncRolloutSample, + SWEAsyncRolloutWorker, + SWEAsyncRolloutWorkerConfig, + build_answer_tool_invoker, + build_openai_chat_forwarder, +) __all__ = [ "SWEAsyncControlPlane", "SWEAsyncControlPlaneConfig", + "SWEAsyncRolloutSample", + "SWEAsyncRolloutWorker", + "SWEAsyncRolloutWorkerConfig", + "build_answer_tool_invoker", "build_hf_space_base_url", + "build_openai_chat_forwarder", ] diff --git a/envs/mini_swe_env/async_grpo/rollout_worker.py b/envs/mini_swe_env/async_grpo/rollout_worker.py new file mode 100644 index 000000000..3999b7153 --- /dev/null +++ b/envs/mini_swe_env/async_grpo/rollout_worker.py @@ -0,0 +1,448 @@ +from __future__ import annotations + +import asyncio +import copy +import inspect +import logging +import queue +import threading +import time +import uuid +from dataclasses import dataclass, field +from typing import Any, Awaitable, Callable, Iterator, Protocol, Sequence, cast + +import requests + +from ..models import SWETask + + +_log = logging.getLogger(__name__) + +VLLMForwarder = Callable[[dict[str, Any]], dict[str, Any] | Awaitable[dict[str, Any]]] +AnswerToolInvoker = Callable[ + [dict[str, Any]], + dict[str, Any] | Awaitable[dict[str, Any]], +] + + +class _SessionProtocol(Protocol): + swe_task: SWETask + + async def next_request(self, timeout_s: float | None = None) -> dict[str, Any] | None: + ... + + async def deliver(self, intercept: dict[str, Any], response_dict: dict[str, Any]) -> None: + ... + + def verify(self, transcript: list[dict[str, Any]], final_state: Any | None = None) -> Any: + ... + + def close(self) -> None: + ... + + +class _SessionFactoryProtocol(Protocol): + def create( + self, + task: Any, + seed: int | None = None, + episode_id: str | None = None, + ) -> _SessionProtocol: + ... + + +@dataclass(frozen=True) +class SWEAsyncRolloutWorkerConfig: + max_inflight_tasks: int = 1 + queue_maxsize: int = 64 + request_timeout_s: float = 180.0 + max_turns: int = 50 + idle_backoff_s: float = 0.2 + queue_put_timeout_s: float = 1.0 + + +@dataclass +class SWEAsyncRolloutSample: + """One rollout item produced by the async worker. + + Field names are chosen to align with AsyncGRPO queue consumption. + """ + + input_ids: list[int] + completion_mask: list[int] + old_log_probs: list[float] + advantages: float + model_version: int + metrics: dict[str, Any] = field(default_factory=dict) + + +class SWEAsyncRolloutWorker: + """Background rollout producer compatible with AsyncGRPO's worker protocol. + + The worker continuously: + - selects a SWE task, + - creates an interception-gated SWE session, + - forwards intercepted LLM requests to a vLLM/OpenAI-compatible endpoint, + - bridges ``answer`` tool calls to host-side grading, + - verifies reward and enqueues a rollout sample. + """ + + def __init__( + self, + *, + session_factory: _SessionFactoryProtocol, + tasks: Sequence[SWETask], + vllm_forwarder: VLLMForwarder, + answer_tool_invoker: AnswerToolInvoker | None = None, + config: SWEAsyncRolloutWorkerConfig | None = None, + ) -> None: + if not tasks: + raise ValueError("tasks must be non-empty") + + self._session_factory = session_factory + self._tasks = list(tasks) + self._vllm_forwarder = vllm_forwarder + self._answer_tool_invoker = answer_tool_invoker + self.config = config or SWEAsyncRolloutWorkerConfig() + + self.rollout_buffer: queue.Queue[SWEAsyncRolloutSample] = queue.Queue( + maxsize=self.config.queue_maxsize + ) + + self._stop_event = threading.Event() + self._pause_event = threading.Event() + self._threads: list[threading.Thread] = [] + self._task_index = 0 + self._state_lock = threading.Lock() + self._started = False + self._model_version = 0 + self._last_weight_sync_param_count = 0 + + def start(self) -> None: + with self._state_lock: + if self._started: + return + self._started = True + + self._stop_event.clear() + self._threads = [] + for idx in range(max(1, self.config.max_inflight_tasks)): + thread = threading.Thread( + target=self._worker_loop, + args=(idx,), + daemon=True, + name=f"swe-async-rollout-{idx}", + ) + thread.start() + self._threads.append(thread) + + _log.info("swe_async_rollout_worker_started threads=%d", len(self._threads)) + + def stop(self) -> None: + self._stop_event.set() + for thread in self._threads: + thread.join(timeout=5.0) + with self._state_lock: + self._started = False + self._threads = [] + _log.info("swe_async_rollout_worker_stopped") + + def pause(self) -> None: + self._pause_event.set() + _log.info("swe_async_rollout_worker_paused") + + def resume(self) -> None: + self._pause_event.clear() + _log.info("swe_async_rollout_worker_resumed") + + def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: + count = 0 + for _ in iterator: + count += 1 + self._last_weight_sync_param_count = count + _log.info("swe_async_rollout_worker_weight_sync_received params=%d", count) + + def update_model_version(self, version: int) -> None: + with self._state_lock: + self._model_version = int(version) + + @property + def last_weight_sync_param_count(self) -> int: + return self._last_weight_sync_param_count + + def stats(self) -> dict[str, Any]: + with self._state_lock: + return { + "started": self._started, + "paused": self._pause_event.is_set(), + "queue_size": self.rollout_buffer.qsize(), + "max_queue_size": self.config.queue_maxsize, + "model_version": self._model_version, + "worker_threads": len(self._threads), + "last_weight_sync_param_count": self._last_weight_sync_param_count, + } + + def _current_model_version(self) -> int: + with self._state_lock: + return self._model_version + + def _next_task(self) -> SWETask: + with self._state_lock: + task = self._tasks[self._task_index % len(self._tasks)] + self._task_index += 1 + return task + + def _wait_if_paused(self) -> bool: + while self._pause_event.is_set() and not self._stop_event.is_set(): + time.sleep(0.05) + return not self._stop_event.is_set() + + def _worker_loop(self, worker_idx: int) -> None: + while not self._stop_event.is_set(): + if not self._wait_if_paused(): + return + + task = self._next_task() + episode_id = f"swe-async-{worker_idx}-{uuid.uuid4().hex[:8]}" + try: + sample = asyncio.run(self._run_one_rollout(task, episode_id=episode_id)) + except Exception: + _log.exception( + "swe_async_rollout_failed worker=%d instance_id=%s", + worker_idx, + task.instance_id, + ) + time.sleep(self.config.idle_backoff_s) + continue + + if sample is None: + time.sleep(self.config.idle_backoff_s) + continue + + try: + self.rollout_buffer.put(sample, timeout=self.config.queue_put_timeout_s) + except queue.Full: + _log.warning( + "swe_async_rollout_queue_full dropping instance_id=%s", task.instance_id + ) + continue + + async def _run_one_rollout( + self, + task: SWETask, + *, + episode_id: str, + ) -> SWEAsyncRolloutSample | None: + session = cast( + _SessionProtocol, + self._session_factory.create(task=task, episode_id=episode_id), + ) + + turns = 0 + answer_called = False + answer_bridged = False + all_token_ids: list[int] = [] + all_logprobs: list[float] = [] + t0 = time.time() + + try: + while turns < self.config.max_turns and not self._stop_event.is_set(): + intercept = await session.next_request( + timeout_s=self.config.request_timeout_s, + ) + if intercept is None: + break + + turns += 1 + response = await self._maybe_call_forwarder(self._vllm_forwarder, intercept) + response = copy.deepcopy(response) + + if _response_has_answer_tool_call(response): + answer_called = True + if self._answer_tool_invoker is not None: + await self._maybe_call_forwarder(self._answer_tool_invoker, intercept) + answer_bridged = True + _strip_answer_tool_calls(response) + + token_ids, logprobs = _extract_logprob_trace(response) + all_token_ids.extend(token_ids) + all_logprobs.extend(logprobs) + + await session.deliver(intercept, response) + + if answer_bridged: + break + + verify = session.verify(transcript=[]) + reward = float(getattr(verify, "env_reward", 0.0) or 0.0) + verify_metrics = dict(getattr(verify, "metrics", {}) or {}) + + if not all_logprobs: + all_logprobs = [0.0] + all_token_ids = [0] + + model_version = self._current_model_version() + sample = SWEAsyncRolloutSample( + input_ids=all_token_ids, + completion_mask=[1] * len(all_logprobs), + old_log_probs=all_logprobs, + advantages=reward, + model_version=model_version, + metrics={ + **verify_metrics, + "instance_id": task.instance_id, + "turns": turns, + "answer_called": answer_called, + "answer_bridged": answer_bridged, + "rollout_wall_time_s": round(time.time() - t0, 3), + }, + ) + return sample + finally: + session.close() + + @staticmethod + async def _maybe_call_forwarder( + forwarder: Callable[[dict[str, Any]], Any], + payload: dict[str, Any], + ) -> dict[str, Any]: + result = forwarder(payload) + if inspect.isawaitable(result): + awaited = await cast(Awaitable[dict[str, Any]], result) + return awaited + return cast(dict[str, Any], result) + + +def build_openai_chat_forwarder( + *, + base_url: str, + api_key: str, + model: str, + request_timeout_s: float = 180.0, +) -> VLLMForwarder: + """Create a sync forwarder for OpenAI-compatible chat completions.""" + + def _forward(intercept: dict[str, Any]) -> dict[str, Any]: + body = dict(intercept.get("body") or {}) + body["model"] = model + body["logprobs"] = True + body["top_logprobs"] = int(body.get("top_logprobs") or 5) + body.pop("stream", None) + body.pop("stream_options", None) + + response = requests.post( + f"{base_url.rstrip('/')}/chat/completions", + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json=body, + timeout=request_timeout_s, + ) + if response.status_code != 200: + raise RuntimeError( + f"vllm forward error {response.status_code}: {response.text[:400]}" + ) + return cast(dict[str, Any], response.json()) + + return _forward + + +def build_answer_tool_invoker( + *, + interception_base_url: str, + interception_auth_token: str, + request_timeout_s: float = 180.0, + extra_headers: dict[str, str] | None = None, +) -> AnswerToolInvoker: + """Create a sync invoker for host-side ``answer`` tool bridging.""" + + def _invoke(intercept: dict[str, Any]) -> dict[str, Any]: + rollout_id = str(intercept.get("rollout_id") or "") + if not rollout_id: + raise RuntimeError("intercept missing rollout_id for answer invocation") + + headers = { + "Authorization": f"Bearer {interception_auth_token}", + "Content-Type": "application/json", + } + if extra_headers: + headers.update(extra_headers) + + response = requests.post( + f"{interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1/tools/answer", + headers=headers, + json={"arguments": {}}, + timeout=request_timeout_s, + ) + if response.status_code != 200: + raise RuntimeError( + f"answer bridge error {response.status_code}: {response.text[:400]}" + ) + return cast(dict[str, Any], response.json()) + + return _invoke + + +def _response_has_answer_tool_call(response: dict[str, Any]) -> bool: + choices = response.get("choices") or [] + if not choices: + return False + first = choices[0] or {} + message = first.get("message") or {} + tool_calls = message.get("tool_calls") or [] + for tool_call in tool_calls: + function = (tool_call or {}).get("function") or {} + if function.get("name") == "answer": + return True + return False + + +def _strip_answer_tool_calls(response: dict[str, Any]) -> None: + choices = list(response.get("choices") or []) + if not choices: + return + + first = dict(choices[0]) + message = dict(first.get("message") or {}) + message.pop("tool_calls", None) + current = str(message.get("content") or "").strip() + suffix = "Submission received and graded on host." + message["content"] = f"{current}\n{suffix}".strip() + first["message"] = message + first["finish_reason"] = "stop" + choices[0] = first + response["choices"] = choices + + +def _extract_logprob_trace(response: dict[str, Any]) -> tuple[list[int], list[float]]: + token_ids: list[int] = [] + logprobs: list[float] = [] + + choices = response.get("choices") or [] + if not choices: + return token_ids, logprobs + + first = choices[0] or {} + content = ((first.get("logprobs") or {}).get("content") or []) + for row in content: + if not isinstance(row, dict): + continue + lp = row.get("logprob") + if isinstance(lp, (int, float)): + logprobs.append(float(lp)) + token_id = row.get("token_id") + token_ids.append(int(token_id) if isinstance(token_id, int) else 0) + + return token_ids, logprobs + + +__all__ = [ + "AnswerToolInvoker", + "SWEAsyncRolloutSample", + "SWEAsyncRolloutWorker", + "SWEAsyncRolloutWorkerConfig", + "VLLMForwarder", + "build_answer_tool_invoker", + "build_openai_chat_forwarder", +] From 01a1dc30c87a10cf938f6f3cec6223468a29b8f5 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 17:32:15 +0530 Subject: [PATCH 33/79] feat: answer tracking and reward source management feat: SWEToolEnv for AsyncGRPO training --- envs/mini_swe_env/async_grpo/__init__.py | 41 +- envs/mini_swe_env/async_grpo/control_plane.py | 168 ------- .../async_grpo/interception_app.py | 54 --- .../mini_swe_env/async_grpo/rollout_worker.py | 448 ------------------ envs/mini_swe_env/async_grpo/swe_tool_env.py | 289 +++++++++++ envs/mini_swe_env/harness.py | 63 ++- examples/mini_swe_env/README.md | 96 ++-- examples/mini_swe_env/train_swe_async_grpo.py | 148 ++++++ 8 files changed, 544 insertions(+), 763 deletions(-) delete mode 100644 envs/mini_swe_env/async_grpo/control_plane.py delete mode 100644 envs/mini_swe_env/async_grpo/interception_app.py delete mode 100644 envs/mini_swe_env/async_grpo/rollout_worker.py create mode 100644 envs/mini_swe_env/async_grpo/swe_tool_env.py create mode 100644 examples/mini_swe_env/train_swe_async_grpo.py diff --git a/envs/mini_swe_env/async_grpo/__init__.py b/envs/mini_swe_env/async_grpo/__init__.py index 05122131d..2e84e8dfb 100644 --- a/envs/mini_swe_env/async_grpo/__init__.py +++ b/envs/mini_swe_env/async_grpo/__init__.py @@ -1,31 +1,24 @@ -"""Async GRPO control-plane primitives for Mini SWE on HF Spaces. +"""Async GRPO training for Mini SWE on HF Spaces. -This package provides control-plane setup utilities: -- configure + launch the interception server on Space-exposed networking, -- keep trainer-facing rollout registration in the same process, -- expose simple runtime stats for leak detection. +This package provides: + +- :class:`SWEToolEnv` — TRL ``environment_factory``-compatible environment + with ``bash()`` and ``answer()`` tools backed by an HF/Docker sandbox. +- :func:`swe_reward` — Reward function that parses grading results from + completion messages. + +These plug directly into TRL's ``AsyncGRPOTrainer`` (or ``GRPOTrainer``). +No custom rollout worker, interception server, or control plane needed — +TRL handles tokenization, generation, logprobs, token IDs, weight sync, +and sample assembly. """ -from .control_plane import ( - SWEAsyncControlPlane, - SWEAsyncControlPlaneConfig, - build_hf_space_base_url, -) -from .rollout_worker import ( - SWEAsyncRolloutSample, - SWEAsyncRolloutWorker, - SWEAsyncRolloutWorkerConfig, - build_answer_tool_invoker, - build_openai_chat_forwarder, +from .swe_tool_env import ( + SWEToolEnv, + swe_reward, ) __all__ = [ - "SWEAsyncControlPlane", - "SWEAsyncControlPlaneConfig", - "SWEAsyncRolloutSample", - "SWEAsyncRolloutWorker", - "SWEAsyncRolloutWorkerConfig", - "build_answer_tool_invoker", - "build_hf_space_base_url", - "build_openai_chat_forwarder", + "SWEToolEnv", + "swe_reward", ] diff --git a/envs/mini_swe_env/async_grpo/control_plane.py b/envs/mini_swe_env/async_grpo/control_plane.py deleted file mode 100644 index f6bd64a2d..000000000 --- a/envs/mini_swe_env/async_grpo/control_plane.py +++ /dev/null @@ -1,168 +0,0 @@ -from __future__ import annotations - -import asyncio -import contextlib -import logging -import os -from dataclasses import dataclass -from typing import Any, Mapping - -from openenv.core.harness.agents.interception_server import InterceptionServer - - -_log = logging.getLogger(__name__) - -_DEFAULT_INTERCEPTION_HOST = "0.0.0.0" -_DEFAULT_INTERCEPTION_PORT = 8765 - - -def build_hf_space_base_url(space_id: str) -> str: - """Return canonical public Space URL from ``owner/name``.""" - owner, sep, name = (space_id or "").strip().partition("/") - if not sep or not owner or not name: - raise ValueError( - "SPACE_ID must be in 'owner/name' format to derive interception base URL" - ) - return f"https://{owner}-{name}.hf.space" - - -@dataclass(frozen=True) -class SWEAsyncControlPlaneConfig: - """Runtime config for the Space-hosted interception control plane.""" - - host: str - port: int - auth_token: str - interception_base_url: str - - @classmethod - def from_env( - cls, - *, - env: Mapping[str, str] | None = None, - require_auth_token: bool = True, - ) -> "SWEAsyncControlPlaneConfig": - source = env or os.environ - - host = source.get("INTERCEPTION_HOST", _DEFAULT_INTERCEPTION_HOST).strip() - port_raw = source.get("INTERCEPTION_PORT") or source.get("PORT") - port = int(port_raw) if port_raw else _DEFAULT_INTERCEPTION_PORT - - token = ( - source.get("INTERCEPTION_AUTH_TOKEN") - or source.get("INTERCEPTION_SECRET") - or "" - ).strip() - if require_auth_token and not token: - raise ValueError( - "Missing interception auth token. Set INTERCEPTION_AUTH_TOKEN in Space secrets." - ) - - base_url = (source.get("INTERCEPTION_BASE_URL") or "").strip().rstrip("/") - if not base_url: - space_host = (source.get("SPACE_HOST") or "").strip().rstrip("/") - if space_host: - if "http" not in space_host: - space_host = f"https://{space_host}" - base_url = space_host.rstrip("/") - else: - space_id = (source.get("SPACE_ID") or "").strip() - if space_id: - base_url = build_hf_space_base_url(space_id) - - if not base_url: - raise ValueError( - "Could not resolve interception_base_url. Set INTERCEPTION_BASE_URL, " - "SPACE_HOST, or SPACE_ID." - ) - - return cls( - host=host, - port=port, - auth_token=token, - interception_base_url=base_url, - ) - - -class SWEAsyncControlPlane: - """Owns the interception server and rollout registration state. - - Trainer-side async rollout workers should use this object directly for - ``register_rollout/get_intercept/unregister_rollout`` so rollout queues and - intercept state remain in-process with the trainer. - """ - - def __init__( - self, - *, - config: SWEAsyncControlPlaneConfig, - server: InterceptionServer | None = None, - ) -> None: - self.config = config - self.server = server or InterceptionServer( - host=config.host, - port=config.port, - secret=config.auth_token, - ) - - @property - def interception_base_url(self) -> str: - return self.config.interception_base_url - - @property - def auth_token(self) -> str: - return self.config.auth_token - - async def start(self) -> None: - await self.server.start() - # If port was 0 in config, InterceptionServer resolves it after bind. - _log.info( - "swe_async_control_plane_started base_url=%s port=%d", - self.interception_base_url, - self.server.port, - ) - - async def stop(self) -> None: - await self.server.stop() - _log.info("swe_async_control_plane_stopped") - - def register_rollout( - self, - rollout_id: str, - *, - state: dict[str, Any] | None = None, - ) -> asyncio.Queue: - queue = self.server.register_rollout(rollout_id, state=state) - stats = self.stats() - _log.info( - "swe_async_rollout_registered rollout_id=%s active_rollouts=%d", - rollout_id, - stats["active_rollouts"], - ) - return queue - - def unregister_rollout(self, rollout_id: str) -> None: - self.server.unregister_rollout(rollout_id) - stats = self.stats() - _log.info( - "swe_async_rollout_unregistered rollout_id=%s active_rollouts=%d", - rollout_id, - stats["active_rollouts"], - ) - - @contextlib.contextmanager - def rollout( - self, - rollout_id: str, - *, - state: dict[str, Any] | None = None, - ): - """Leak-safe context manager for one rollout registration.""" - queue = self.register_rollout(rollout_id, state=state) - try: - yield queue - finally: - self.unregister_rollout(rollout_id) - - def stats(self) -> dict[str, int]: - return self.server.stats() diff --git a/envs/mini_swe_env/async_grpo/interception_app.py b/envs/mini_swe_env/async_grpo/interception_app.py deleted file mode 100644 index 7bae5216e..000000000 --- a/envs/mini_swe_env/async_grpo/interception_app.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Entrypoint for Space-hosted interception control plane. - -Run inside the HF Space process that should expose interception endpoints: - - PYTHONPATH=src:envs python -m mini_swe_env.async_grpo.interception_app -""" - -from __future__ import annotations - -import asyncio -import logging -import signal - -from .control_plane import SWEAsyncControlPlane, SWEAsyncControlPlaneConfig - - -_log = logging.getLogger(__name__) - - -async def run_forever(config: SWEAsyncControlPlaneConfig | None = None) -> None: - cfg = config or SWEAsyncControlPlaneConfig.from_env() - plane = SWEAsyncControlPlane(config=cfg) - await plane.start() - - stop_event = asyncio.Event() - - def _request_stop() -> None: - if not stop_event.is_set(): - _log.info("swe_async_control_plane_shutdown_requested") - stop_event.set() - - loop = asyncio.get_running_loop() - for sig in (signal.SIGINT, signal.SIGTERM): - try: - loop.add_signal_handler(sig, _request_stop) - except NotImplementedError: - pass - - try: - await stop_event.wait() - finally: - await plane.stop() - - -def main() -> None: - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s %(levelname)s %(name)s %(message)s", - ) - asyncio.run(run_forever()) - - -if __name__ == "__main__": - main() diff --git a/envs/mini_swe_env/async_grpo/rollout_worker.py b/envs/mini_swe_env/async_grpo/rollout_worker.py deleted file mode 100644 index 3999b7153..000000000 --- a/envs/mini_swe_env/async_grpo/rollout_worker.py +++ /dev/null @@ -1,448 +0,0 @@ -from __future__ import annotations - -import asyncio -import copy -import inspect -import logging -import queue -import threading -import time -import uuid -from dataclasses import dataclass, field -from typing import Any, Awaitable, Callable, Iterator, Protocol, Sequence, cast - -import requests - -from ..models import SWETask - - -_log = logging.getLogger(__name__) - -VLLMForwarder = Callable[[dict[str, Any]], dict[str, Any] | Awaitable[dict[str, Any]]] -AnswerToolInvoker = Callable[ - [dict[str, Any]], - dict[str, Any] | Awaitable[dict[str, Any]], -] - - -class _SessionProtocol(Protocol): - swe_task: SWETask - - async def next_request(self, timeout_s: float | None = None) -> dict[str, Any] | None: - ... - - async def deliver(self, intercept: dict[str, Any], response_dict: dict[str, Any]) -> None: - ... - - def verify(self, transcript: list[dict[str, Any]], final_state: Any | None = None) -> Any: - ... - - def close(self) -> None: - ... - - -class _SessionFactoryProtocol(Protocol): - def create( - self, - task: Any, - seed: int | None = None, - episode_id: str | None = None, - ) -> _SessionProtocol: - ... - - -@dataclass(frozen=True) -class SWEAsyncRolloutWorkerConfig: - max_inflight_tasks: int = 1 - queue_maxsize: int = 64 - request_timeout_s: float = 180.0 - max_turns: int = 50 - idle_backoff_s: float = 0.2 - queue_put_timeout_s: float = 1.0 - - -@dataclass -class SWEAsyncRolloutSample: - """One rollout item produced by the async worker. - - Field names are chosen to align with AsyncGRPO queue consumption. - """ - - input_ids: list[int] - completion_mask: list[int] - old_log_probs: list[float] - advantages: float - model_version: int - metrics: dict[str, Any] = field(default_factory=dict) - - -class SWEAsyncRolloutWorker: - """Background rollout producer compatible with AsyncGRPO's worker protocol. - - The worker continuously: - - selects a SWE task, - - creates an interception-gated SWE session, - - forwards intercepted LLM requests to a vLLM/OpenAI-compatible endpoint, - - bridges ``answer`` tool calls to host-side grading, - - verifies reward and enqueues a rollout sample. - """ - - def __init__( - self, - *, - session_factory: _SessionFactoryProtocol, - tasks: Sequence[SWETask], - vllm_forwarder: VLLMForwarder, - answer_tool_invoker: AnswerToolInvoker | None = None, - config: SWEAsyncRolloutWorkerConfig | None = None, - ) -> None: - if not tasks: - raise ValueError("tasks must be non-empty") - - self._session_factory = session_factory - self._tasks = list(tasks) - self._vllm_forwarder = vllm_forwarder - self._answer_tool_invoker = answer_tool_invoker - self.config = config or SWEAsyncRolloutWorkerConfig() - - self.rollout_buffer: queue.Queue[SWEAsyncRolloutSample] = queue.Queue( - maxsize=self.config.queue_maxsize - ) - - self._stop_event = threading.Event() - self._pause_event = threading.Event() - self._threads: list[threading.Thread] = [] - self._task_index = 0 - self._state_lock = threading.Lock() - self._started = False - self._model_version = 0 - self._last_weight_sync_param_count = 0 - - def start(self) -> None: - with self._state_lock: - if self._started: - return - self._started = True - - self._stop_event.clear() - self._threads = [] - for idx in range(max(1, self.config.max_inflight_tasks)): - thread = threading.Thread( - target=self._worker_loop, - args=(idx,), - daemon=True, - name=f"swe-async-rollout-{idx}", - ) - thread.start() - self._threads.append(thread) - - _log.info("swe_async_rollout_worker_started threads=%d", len(self._threads)) - - def stop(self) -> None: - self._stop_event.set() - for thread in self._threads: - thread.join(timeout=5.0) - with self._state_lock: - self._started = False - self._threads = [] - _log.info("swe_async_rollout_worker_stopped") - - def pause(self) -> None: - self._pause_event.set() - _log.info("swe_async_rollout_worker_paused") - - def resume(self) -> None: - self._pause_event.clear() - _log.info("swe_async_rollout_worker_resumed") - - def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: - count = 0 - for _ in iterator: - count += 1 - self._last_weight_sync_param_count = count - _log.info("swe_async_rollout_worker_weight_sync_received params=%d", count) - - def update_model_version(self, version: int) -> None: - with self._state_lock: - self._model_version = int(version) - - @property - def last_weight_sync_param_count(self) -> int: - return self._last_weight_sync_param_count - - def stats(self) -> dict[str, Any]: - with self._state_lock: - return { - "started": self._started, - "paused": self._pause_event.is_set(), - "queue_size": self.rollout_buffer.qsize(), - "max_queue_size": self.config.queue_maxsize, - "model_version": self._model_version, - "worker_threads": len(self._threads), - "last_weight_sync_param_count": self._last_weight_sync_param_count, - } - - def _current_model_version(self) -> int: - with self._state_lock: - return self._model_version - - def _next_task(self) -> SWETask: - with self._state_lock: - task = self._tasks[self._task_index % len(self._tasks)] - self._task_index += 1 - return task - - def _wait_if_paused(self) -> bool: - while self._pause_event.is_set() and not self._stop_event.is_set(): - time.sleep(0.05) - return not self._stop_event.is_set() - - def _worker_loop(self, worker_idx: int) -> None: - while not self._stop_event.is_set(): - if not self._wait_if_paused(): - return - - task = self._next_task() - episode_id = f"swe-async-{worker_idx}-{uuid.uuid4().hex[:8]}" - try: - sample = asyncio.run(self._run_one_rollout(task, episode_id=episode_id)) - except Exception: - _log.exception( - "swe_async_rollout_failed worker=%d instance_id=%s", - worker_idx, - task.instance_id, - ) - time.sleep(self.config.idle_backoff_s) - continue - - if sample is None: - time.sleep(self.config.idle_backoff_s) - continue - - try: - self.rollout_buffer.put(sample, timeout=self.config.queue_put_timeout_s) - except queue.Full: - _log.warning( - "swe_async_rollout_queue_full dropping instance_id=%s", task.instance_id - ) - continue - - async def _run_one_rollout( - self, - task: SWETask, - *, - episode_id: str, - ) -> SWEAsyncRolloutSample | None: - session = cast( - _SessionProtocol, - self._session_factory.create(task=task, episode_id=episode_id), - ) - - turns = 0 - answer_called = False - answer_bridged = False - all_token_ids: list[int] = [] - all_logprobs: list[float] = [] - t0 = time.time() - - try: - while turns < self.config.max_turns and not self._stop_event.is_set(): - intercept = await session.next_request( - timeout_s=self.config.request_timeout_s, - ) - if intercept is None: - break - - turns += 1 - response = await self._maybe_call_forwarder(self._vllm_forwarder, intercept) - response = copy.deepcopy(response) - - if _response_has_answer_tool_call(response): - answer_called = True - if self._answer_tool_invoker is not None: - await self._maybe_call_forwarder(self._answer_tool_invoker, intercept) - answer_bridged = True - _strip_answer_tool_calls(response) - - token_ids, logprobs = _extract_logprob_trace(response) - all_token_ids.extend(token_ids) - all_logprobs.extend(logprobs) - - await session.deliver(intercept, response) - - if answer_bridged: - break - - verify = session.verify(transcript=[]) - reward = float(getattr(verify, "env_reward", 0.0) or 0.0) - verify_metrics = dict(getattr(verify, "metrics", {}) or {}) - - if not all_logprobs: - all_logprobs = [0.0] - all_token_ids = [0] - - model_version = self._current_model_version() - sample = SWEAsyncRolloutSample( - input_ids=all_token_ids, - completion_mask=[1] * len(all_logprobs), - old_log_probs=all_logprobs, - advantages=reward, - model_version=model_version, - metrics={ - **verify_metrics, - "instance_id": task.instance_id, - "turns": turns, - "answer_called": answer_called, - "answer_bridged": answer_bridged, - "rollout_wall_time_s": round(time.time() - t0, 3), - }, - ) - return sample - finally: - session.close() - - @staticmethod - async def _maybe_call_forwarder( - forwarder: Callable[[dict[str, Any]], Any], - payload: dict[str, Any], - ) -> dict[str, Any]: - result = forwarder(payload) - if inspect.isawaitable(result): - awaited = await cast(Awaitable[dict[str, Any]], result) - return awaited - return cast(dict[str, Any], result) - - -def build_openai_chat_forwarder( - *, - base_url: str, - api_key: str, - model: str, - request_timeout_s: float = 180.0, -) -> VLLMForwarder: - """Create a sync forwarder for OpenAI-compatible chat completions.""" - - def _forward(intercept: dict[str, Any]) -> dict[str, Any]: - body = dict(intercept.get("body") or {}) - body["model"] = model - body["logprobs"] = True - body["top_logprobs"] = int(body.get("top_logprobs") or 5) - body.pop("stream", None) - body.pop("stream_options", None) - - response = requests.post( - f"{base_url.rstrip('/')}/chat/completions", - headers={ - "Authorization": f"Bearer {api_key}", - "Content-Type": "application/json", - }, - json=body, - timeout=request_timeout_s, - ) - if response.status_code != 200: - raise RuntimeError( - f"vllm forward error {response.status_code}: {response.text[:400]}" - ) - return cast(dict[str, Any], response.json()) - - return _forward - - -def build_answer_tool_invoker( - *, - interception_base_url: str, - interception_auth_token: str, - request_timeout_s: float = 180.0, - extra_headers: dict[str, str] | None = None, -) -> AnswerToolInvoker: - """Create a sync invoker for host-side ``answer`` tool bridging.""" - - def _invoke(intercept: dict[str, Any]) -> dict[str, Any]: - rollout_id = str(intercept.get("rollout_id") or "") - if not rollout_id: - raise RuntimeError("intercept missing rollout_id for answer invocation") - - headers = { - "Authorization": f"Bearer {interception_auth_token}", - "Content-Type": "application/json", - } - if extra_headers: - headers.update(extra_headers) - - response = requests.post( - f"{interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1/tools/answer", - headers=headers, - json={"arguments": {}}, - timeout=request_timeout_s, - ) - if response.status_code != 200: - raise RuntimeError( - f"answer bridge error {response.status_code}: {response.text[:400]}" - ) - return cast(dict[str, Any], response.json()) - - return _invoke - - -def _response_has_answer_tool_call(response: dict[str, Any]) -> bool: - choices = response.get("choices") or [] - if not choices: - return False - first = choices[0] or {} - message = first.get("message") or {} - tool_calls = message.get("tool_calls") or [] - for tool_call in tool_calls: - function = (tool_call or {}).get("function") or {} - if function.get("name") == "answer": - return True - return False - - -def _strip_answer_tool_calls(response: dict[str, Any]) -> None: - choices = list(response.get("choices") or []) - if not choices: - return - - first = dict(choices[0]) - message = dict(first.get("message") or {}) - message.pop("tool_calls", None) - current = str(message.get("content") or "").strip() - suffix = "Submission received and graded on host." - message["content"] = f"{current}\n{suffix}".strip() - first["message"] = message - first["finish_reason"] = "stop" - choices[0] = first - response["choices"] = choices - - -def _extract_logprob_trace(response: dict[str, Any]) -> tuple[list[int], list[float]]: - token_ids: list[int] = [] - logprobs: list[float] = [] - - choices = response.get("choices") or [] - if not choices: - return token_ids, logprobs - - first = choices[0] or {} - content = ((first.get("logprobs") or {}).get("content") or []) - for row in content: - if not isinstance(row, dict): - continue - lp = row.get("logprob") - if isinstance(lp, (int, float)): - logprobs.append(float(lp)) - token_id = row.get("token_id") - token_ids.append(int(token_id) if isinstance(token_id, int) else 0) - - return token_ids, logprobs - - -__all__ = [ - "AnswerToolInvoker", - "SWEAsyncRolloutSample", - "SWEAsyncRolloutWorker", - "SWEAsyncRolloutWorkerConfig", - "VLLMForwarder", - "build_answer_tool_invoker", - "build_openai_chat_forwarder", -] diff --git a/envs/mini_swe_env/async_grpo/swe_tool_env.py b/envs/mini_swe_env/async_grpo/swe_tool_env.py new file mode 100644 index 000000000..7ea5fc99e --- /dev/null +++ b/envs/mini_swe_env/async_grpo/swe_tool_env.py @@ -0,0 +1,289 @@ +"""SWE tool environment for TRL AsyncGRPO / GRPO training. + +Implements the ``environment_factory`` protocol expected by +``AsyncGRPOTrainer`` and ``GRPOTrainer``: + +- ``reset(**row)`` — create a sandbox from the SWE-Gym per-task image. +- ``bash(command)`` — execute a shell command in ``/testbed``. +- ``answer()`` — run SWE-Gym grading and return resolved/not. + +TRL discovers public methods as tools, drives the model's generation +loop, handles tokenization, logprobs, token IDs, weight sync, and +sample assembly. We only provide the environment. + +Usage with ``AsyncGRPOTrainer``:: + + from mini_swe_env.async_grpo import SWEToolEnv, swe_reward + + trainer = AsyncGRPOTrainer( + model="Qwen/Qwen3-1.7B", + reward_funcs=swe_reward, + train_dataset=dataset, + environment_factory=SWEToolEnv.factory(backend), + ) + trainer.train() +""" + +from __future__ import annotations + +import json +import logging +import shlex +from typing import Any, Callable + +from ..grading import grade_from_case_results +from ..models import SWEGymTask, SWETask + +_log = logging.getLogger(__name__) + +TESTBED = "/testbed" +VERIFY_TIMEOUT_S = 300 + + +class SWEToolEnv: + """TRL-compatible SWE environment with ``bash`` and ``answer`` tools. + + One instance is created per ``max_inflight_tasks`` slot. TRL calls + ``reset(**row)`` before each generation, then the model calls + ``bash(command=...)`` and ``answer()`` as tool calls. + """ + + def __init__(self, sandbox_backend: Any) -> None: + self._backend = sandbox_backend + self._sandbox: Any | None = None + self._task: SWETask | None = None + self._gym_task: SWEGymTask | None = None + self.reward: float = 0.0 + self.resolved: bool = False + self._answer_called: bool = False + + # ── TRL lifecycle ────────────────────────────────────────────── + + def reset(self, **kwargs: Any) -> str | None: + """Create a fresh sandbox for the task described in *kwargs*. + + Called by TRL before each generation. Receives all columns from + the dataset row as keyword arguments. + """ + self._cleanup() + self.reward = 0.0 + self.resolved = False + self._answer_called = False + + task_json = kwargs.get("task_json", "") + if not task_json: + return None + + raw = json.loads(task_json) + self._gym_task = SWEGymTask(**raw) + self._task = self._gym_task.to_swe_task() + + self._sandbox = self._backend.create( + timeout_s=self._task.timeout_s, + image=self._task.sandbox_image, + ) + + return None + + # ── Tools (discovered by TRL via inspect.getmembers) ────────── + + def bash(self, command: str) -> str: + """Execute a shell command in the repository at /testbed. + + Args: + command: Shell command to run. + + Returns: + Combined stdout and stderr output. + """ + if self._sandbox is None: + return "Error: no sandbox — call reset() first." + result = self._sandbox.exec(command, cwd=TESTBED, timeout=VERIFY_TIMEOUT_S) + stdout = (result.stdout or "")[-8000:] + stderr = (result.stderr or "")[-4000:] + if stderr: + return f"{stdout}\nSTDERR:\n{stderr}" + return stdout + + def answer(self) -> str: + """Submit your solution for grading. + + Runs the SWE-Gym test suite (FAIL_TO_PASS / PASS_TO_PASS) and + returns whether the issue is resolved. Can only be called once. + + Returns: + A string indicating whether the issue was resolved. + """ + if self._answer_called: + return "Error: answer() already called." + self._answer_called = True + + if self._sandbox is None or self._gym_task is None: + self.reward = 0.0 + self.resolved = False + return "Error: no sandbox or task — call reset() first." + + try: + reward, resolved = _grade_submission( + self._sandbox, self._task, self._gym_task, + ) + self.reward = reward + self.resolved = resolved + return f"Resolved: {str(resolved).lower()}" + except Exception as exc: + _log.exception("SWE grading failed for %s", self._gym_task.instance_id) + self.reward = 0.0 + self.resolved = False + return f"Grading error: {type(exc).__name__}: {exc}" + + # ── Cleanup ──────────────────────────────────────────────────── + + def _cleanup(self) -> None: + if self._sandbox is not None: + try: + self._sandbox.kill() + except Exception: + pass + self._sandbox = None + self._task = None + self._gym_task = None + + def __del__(self) -> None: + self._cleanup() + + # ── Factory helper ───────────────────────────────────────────── + + @staticmethod + def factory(sandbox_backend: Any) -> Callable[[], "SWEToolEnv"]: + """Return an ``environment_factory`` callable for TRL.""" + def _create() -> SWEToolEnv: + return SWEToolEnv(sandbox_backend) + return _create + + +# ── Reward function ──────────────────────────────────────────────────── + + +def swe_reward( + completions: list[Any], + **kwargs: Any, +) -> list[float]: + """Reward function for SWE training. + + Scans each completion for an ``answer`` tool result message and + parses ``"Resolved: true"`` / ``"Resolved: false"``. + + Works with both ``GRPOTrainer`` (receives ``environments=``) and + ``AsyncGRPOTrainer`` (does not — parses completion messages). + """ + # If GRPOTrainer passes environments directly, use them. + environments = kwargs.get("environments") + if environments is not None: + return [float(getattr(env, "reward", 0.0)) for env in environments] + + # AsyncGRPOTrainer path: parse completion messages. + rewards: list[float] = [] + for completion in completions: + reward = 0.0 + if isinstance(completion, list): + for msg in completion: + if not isinstance(msg, dict): + continue + if msg.get("role") == "tool" and msg.get("name") == "answer": + content = str(msg.get("content", "")) + if "resolved: true" in content.lower(): + reward = 1.0 + break + rewards.append(reward) + return rewards + + +# ── Grading (extracted from harness.py) ──────────────────────────────── + + +def _grade_submission( + sandbox: Any, + task: SWETask, + gym_task: SWEGymTask, +) -> tuple[float, bool]: + """Run SWE-Gym grading: revert test files, apply test_patch, run tests.""" + touched_files = _extract_paths_from_test_patch(gym_task.test_patch) + + # Revert test files to base_commit state before applying test_patch. + _revert_test_files(sandbox, base_commit=task.base_commit, paths=touched_files) + + # Apply the test patch. + patch_path = "/tmp/.openenv_swe_test_patch.diff" + sandbox.write_text(patch_path, gym_task.test_patch) + result = sandbox.exec( + f"git apply --whitespace=nowarn {shlex.quote(patch_path)}", + cwd=TESTBED, + timeout=30, + ) + if result.exit_code != 0: + raise RuntimeError( + f"failed to apply test_patch: {(result.stderr or result.stdout or '').strip()}" + ) + + # Run each test case. + cases: list[str] = [] + seen: set[str] = set() + for case in [*gym_task.FAIL_TO_PASS, *gym_task.PASS_TO_PASS]: + if case not in seen: + seen.add(case) + cases.append(case) + + case_results: dict[str, bool] = {} + for case in cases: + cmd = f"python -m pytest -q --maxfail=1 {shlex.quote(case)}" + run = sandbox.exec(cmd, cwd=TESTBED, timeout=VERIFY_TIMEOUT_S) + case_results[case] = run.exit_code == 0 + + grade = grade_from_case_results(gym_task, case_results) + + # Best-effort cleanup. + try: + _revert_test_files(sandbox, base_commit=task.base_commit, paths=touched_files) + except Exception: + pass + + return float(grade.reward), bool(grade.resolved) + + +def _extract_paths_from_test_patch(test_patch: str) -> list[str]: + paths: list[str] = [] + for line in (test_patch or "").splitlines(): + if line.startswith("+++ b/"): + path = line[len("+++ b/"):].strip() + if path and path != "/dev/null": + paths.append(path) + return sorted(set(paths)) + + +def _revert_test_files( + sandbox: Any, + *, + base_commit: str, + paths: list[str], +) -> None: + for path in paths: + has_file = sandbox.exec( + f"git cat-file -e {shlex.quote(f'{base_commit}:{path}')}", + cwd=TESTBED, + timeout=10, + ) + if has_file.exit_code == 0: + cmd = f"git checkout --quiet {shlex.quote(base_commit)} -- {shlex.quote(path)}" + else: + cmd = f"rm -f -- {shlex.quote(path)}" + result = sandbox.exec(cmd, cwd=TESTBED, timeout=20) + if result.exit_code != 0: + raise RuntimeError( + f"failed to revert {path}: {(result.stderr or result.stdout or '').strip()}" + ) + + +__all__ = [ + "SWEToolEnv", + "swe_reward", +] diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index c85ea6f0c..a76f2e360 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -193,6 +193,9 @@ def __init__( self._swe_task = swe_task self._verify_timeout_s = verify_timeout_s self._answer_reward: float | None = None # set by host-side answer tool + self._answer_reward_source: str | None = None + self._answer_called = False + self._answer_bridged = False @property def swe_task(self) -> SWETask: @@ -202,9 +205,33 @@ def swe_task(self) -> SWETask: def answer_reward(self) -> float | None: return self._answer_reward - def set_answer_reward(self, reward: float) -> None: + @property + def answer_reward_source(self) -> str | None: + return self._answer_reward_source + + @property + def answer_called(self) -> bool: + return self._answer_called + + @property + def answer_bridged(self) -> bool: + return self._answer_bridged + + def mark_answer_called(self) -> None: + self._answer_called = True + + def mark_answer_bridged(self) -> None: + self._answer_bridged = True + + def set_answer_reward( + self, + reward: float, + *, + source: str = "host_answer_tool", + ) -> None: """Called by the host-side answer tool handler to store the reward.""" self._answer_reward = reward + self._answer_reward_source = source def initial_messages(self) -> list[Message]: """Return the SWE instruction as the initial prompt.""" @@ -231,14 +258,33 @@ def verify( done=True, metrics={ "instance_id": self._swe_task.instance_id, - "reward_source": "host_answer_tool", + "reward_source": self._answer_reward_source + or "host_answer_tool", + "answer_called": self._answer_called, + "answer_bridged": self._answer_bridged, }, artifacts={ "task_id": self._swe_task.task_id, }, ) - # 2. Fallback: run verify commands (legacy tasks with shell commands). + # 2. Guardrail: answer was attempted but host-side reward not recorded. + if self._answer_called: + return VerifyResult( + env_reward=0.0, + done=True, + metrics={ + "instance_id": self._swe_task.instance_id, + "reward_source": "answer_called_missing_host_reward", + "answer_called": True, + "answer_bridged": self._answer_bridged, + }, + artifacts={ + "task_id": self._swe_task.task_id, + }, + ) + + # 3. Fallback: run verify commands (legacy tasks with shell commands). if self._swe_task.verify: passed = 0 verify_details: list[dict[str, Any]] = [] @@ -276,6 +322,8 @@ def verify( "verify_total": len(self._swe_task.verify), "instance_id": self._swe_task.instance_id, "reward_source": "verify_commands", + "answer_called": False, + "answer_bridged": False, }, artifacts={ "verify_details": verify_details, @@ -283,13 +331,15 @@ def verify( }, ) - # 3. No reward source — agent didn't call answer, no verify cmds. + # 4. No reward source — agent didn't call answer, no verify cmds. return VerifyResult( env_reward=0.0, done=True, metrics={ "instance_id": self._swe_task.instance_id, "reward_source": "default_no_answer", + "answer_called": False, + "answer_bridged": False, }, artifacts={ "task_id": self._swe_task.task_id, @@ -545,6 +595,9 @@ def _register_answer_tool(self, session: SWESession) -> None: async def _answer_handler(arguments: dict[str, Any]) -> dict[str, Any]: del arguments + session.mark_answer_called() + session.mark_answer_bridged() + if session.answer_reward is not None: resolved = session.answer_reward >= 1.0 return { @@ -561,7 +614,7 @@ async def _answer_handler(arguments: dict[str, Any]) -> dict[str, Any]: session.sandbox, session.swe_task, ) - session.set_answer_reward(reward) + session.set_answer_reward(reward, source="host_answer_tool") return { "content": [ { diff --git a/examples/mini_swe_env/README.md b/examples/mini_swe_env/README.md index 6b5a3c39f..f5958e98e 100644 --- a/examples/mini_swe_env/README.md +++ b/examples/mini_swe_env/README.md @@ -12,7 +12,7 @@ using the OpenEnv harness infrastructure. ``` ┌─────────────────────────────────────────────────────────────┐ -│ train_swe_grpo.py │ +│ train_swe_async_grpo.py │ │ │ │ 1. Load SWE-Gym tasks from HuggingFace │ │ 2. Start InterceptionServer on trainer host │ @@ -23,7 +23,7 @@ using the OpenEnv harness infrastructure. │ d. Delivers response back to Pi │ │ e. Repeat until Pi exits or calls answer() │ │ f. session.verify() → host-side SWE-Gym grading │ -│ 4. Compute GRPO advantages and update policy │ +│ 4. AsyncGRPOTrainer consumes queue + updates policy │ └─────────────────────────────────────────────────────────────┘ ``` @@ -44,44 +44,13 @@ The agent **cannot** influence the training reward. The in-sandbox | File | Purpose | |------|---------| -| `config.py` | All training knobs (`SWETrainingConfig`) | -| `smoke_swe.py` | Single-task smoke test (3 modes) | | `run_swe_sample.py` | Real end-to-end interception rollout with a live LLM | -| `train_swe_grpo.py` | GRPO training loop (reference impl) | +| `train_swe_async_grpo.py` | AsyncGRPO trainer entrypoint wired to SWE custom rollout worker | +| `envs/mini_swe_env/async_grpo/` | Control plane, worker, and trainer wiring modules | ## Quick Start -### 1. Dry Run (no sandbox needed) - -Validates config and task loading: - -```bash -PYTHONPATH=src:envs python examples/mini_swe_env/smoke_swe.py --dry-run -``` - -### 2. Smoke Test — Interception Gate (no LLM needed) - -Spins up InterceptionServer, creates a real sandbox, and auto-responds -to prove the full pipeline works: - -```bash -PYTHONPATH=src:envs python examples/mini_swe_env/smoke_swe.py --interception -``` - -Requires Docker and the per-task SWE-Gym image to be pullable. - -### 3. Smoke Test — Black Box (requires LLM endpoint) - -Pi talks directly to an LLM endpoint: - -```bash -SWE_BASE_URL=http://localhost:8000/v1 \ -SWE_API_KEY=test \ -SWE_MODEL=qwen3-8b \ -PYTHONPATH=src:envs python examples/mini_swe_env/smoke_swe.py -``` - -### 4. Real end-to-end run (live LLM) +### 1. Real end-to-end interception rollout (live LLM) ```bash SWE_LLM_BASE_URL=https://api.openai.com/v1 \ @@ -97,42 +66,41 @@ Notes: - If `--assert-host-answer` is set, the script fails unless reward comes from the host-side `answer` tool path (`reward_source=host_answer_tool`). -### 5. GRPO Training (reference) +### 2. AsyncGRPO training (Track D wiring) ```bash -PYTHONPATH=src:envs python examples/mini_swe_env/train_swe_grpo.py --smoke +SWE_ASYNC_MODEL=Qwen/Qwen3-8B \ +SWE_LLM_BASE_URL=http://127.0.0.1:8000/v1 \ +SWE_LLM_API_KEY=test \ +SWE_LLM_MODEL=Qwen/Qwen3-8B \ +INTERCEPTION_AUTH_TOKEN=... \ +INTERCEPTION_BASE_URL=https://.hf.space \ +PYTHONPATH=src:envs uv run python examples/mini_swe_env/train_swe_async_grpo.py \ + --task-variant lite --max-tasks 1 --sandbox-backend hf --max-steps 2 ``` -For full training, adjust `config.py` or use environment variables: - -```bash -SWE_TASK_VARIANT=lite \ -SWE_MODEL=Qwen/Qwen3-32B \ -SWE_GRPO_BATCH_SIZE=4 \ -SWE_SANDBOX_BACKEND=docker \ -PYTHONPATH=src:envs python examples/mini_swe_env/train_swe_grpo.py -``` +Notes: +- Use separate GPUs for vLLM and trainer, per TRL async docs. +- `INTERCEPTION_BASE_URL` must be reachable from HF sandboxes. ## Configuration -All settings are in `config.py` (`SWETrainingConfig`). Override via -`SWE_*` environment variables: +Main env vars for `train_swe_async_grpo.py`: | Env Variable | Default | Description | |-------------|---------|-------------| -| `SWE_TASK_VARIANT` | `lite` | `"lite"` (230) or `"full"` (2,438 tasks) | -| `SWE_MODEL` | `Qwen/Qwen3-8B` | HuggingFace model id | -| `SWE_BASE_URL` | *(empty)* | LLM endpoint (black-box mode) | -| `SWE_API_KEY` | *(empty)* | LLM bearer token | -| `SWE_SANDBOX_BACKEND` | `docker` | `"docker"`, `"e2b"`, or `"hf"` | -| `SWE_INTERCEPTION_PORT` | `9090` | InterceptionServer port | -| `SWE_INTERCEPTION_BASE_URL` | auto | URL reachable from sandbox | -| `SWE_AGENT_TIMEOUT` | `1800` | Agent timeout (seconds) | -| `SWE_MAX_TURNS` | `30` | Max agent turns per rollout | -| `SWE_GRPO_BATCH_SIZE` | `4` | Tasks per GRPO batch | -| `SWE_GRPO_LR` | `1e-6` | Learning rate | -| `SWE_GRPO_BETA` | `0.04` | KL penalty | -| `SWE_MAX_TASKS` | *(all)* | Cap tasks for debugging | +| `SWE_ASYNC_MODEL` | *(required)* | Policy model id for `AsyncGRPOTrainer`. | +| `SWE_LLM_BASE_URL` | *(required)* | OpenAI-compatible generation endpoint (typically vLLM `/v1`). | +| `SWE_LLM_API_KEY` | *(required)* | Bearer token for the generation endpoint. | +| `SWE_LLM_MODEL` | *(required)* | Model id exposed by the generation endpoint. | +| `INTERCEPTION_AUTH_TOKEN` | *(required)* | Auth token for interception server + sandbox client wiring. | +| `INTERCEPTION_BASE_URL` | *(required for remote sandboxes)* | Public URL reachable from HF/E2B sandboxes. | +| `SWE_ASYNC_MAX_STALENESS` | `2` | Async sample staleness bound. | +| `SWE_ASYNC_WEIGHT_SYNC_STEPS` | `1` | Weight sync interval. | +| `SWE_ASYNC_MAX_INFLIGHT_TASKS` | `2` | Inflight rollout concurrency. | +| `SWE_ASYNC_QUEUE_MAXSIZE` | `64` | Rollout queue size cap. | +| `SWE_ASYNC_MAX_STEPS` | `10` | Trainer step budget (override with CLI `--max-steps`). | +| `HF_TOKEN` | *(optional)* | Needed when calling private Space URLs from trainer-side answer bridge. | ## Sandbox Connectivity @@ -140,8 +108,8 @@ The InterceptionServer runs on the trainer host. The sandbox must be able to reach it: - **Docker (same host)**: Automatic — uses `http://host.docker.internal:` -- **Remote sandbox (E2B, HF)**: You must provide a tunnel URL via - `SWE_INTERCEPTION_BASE_URL`. Options: [bore](https://github.com/ekzhang/bore), +- **Remote sandbox (E2B, HF)**: You must provide a tunnel/public URL via + `INTERCEPTION_BASE_URL`. Options: [bore](https://github.com/ekzhang/bore), [ngrok](https://ngrok.com/), [frp](https://github.com/fatedier/frp), or a public IP. diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py new file mode 100644 index 000000000..75763c035 --- /dev/null +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""Train SWE with TRL AsyncGRPOTrainer + environment_factory. + +Uses TRL's built-in AsyncRolloutWorker which handles: +- Tokenization via apply_chat_template +- Generation via /v1/completions with exact token_ids + logprobs +- Multi-turn tool calling (bash, answer) +- NCCL weight sync to vLLM +- Sample assembly (prompt_ids + completion_ids) + +We only provide: +- SWEToolEnv: environment with bash() and answer() tools +- swe_reward: parses completion messages for grading result +- Dataset: SWE-Gym tasks as prompts + +Prerequisites: + - vLLM server running on a separate GPU + - HF Sandbox or Docker backend available + +Example: + # GPU 0: vLLM + CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-1.7B \\ + --tensor-parallel-size 1 --max-model-len 4096 + + # GPU 1: Trainer + CUDA_VISIBLE_DEVICES=1 \\ + SWE_MODEL=Qwen/Qwen3-1.7B \\ + PYTHONPATH=src:envs python examples/mini_swe_env/train_swe_async_grpo.py \\ + --task-variant lite --max-tasks 5 --max-steps 10 +""" + +from __future__ import annotations + +import argparse +import json +import logging +import os +import sys +from pathlib import Path + +_root = Path(__file__).resolve().parent.parent.parent +for _p in (_root / "src", _root / "envs"): + if str(_p) not in sys.path: + sys.path.insert(0, str(_p)) + +from datasets import Dataset # noqa: E402 +from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer # noqa: E402 + +from mini_swe_env.async_grpo.swe_tool_env import SWEToolEnv, swe_reward # noqa: E402 +from mini_swe_env.task_loader_swegym import load_swegym_tasks # noqa: E402 +from openenv.core.harness.sandbox import create_sandbox_backend # noqa: E402 + + +_log = logging.getLogger("swe-async-grpo") + + +def _arg_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser(description="Train SWE with TRL AsyncGRPO") + p.add_argument("--task-variant", default="lite", choices=["lite", "full"]) + p.add_argument("--max-tasks", type=int, default=5) + p.add_argument("--max-steps", type=int, default=10) + p.add_argument("--max-turns", type=int, default=30) + p.add_argument("--sandbox-backend", default="hf", choices=["docker", "e2b", "hf"]) + p.add_argument("--vllm-url", default="http://localhost:8000") + return p + + +def _must_env(name: str) -> str: + value = os.environ.get(name, "").strip() + if not value: + raise RuntimeError(f"Missing required env var: {name}") + return value + + +def main() -> int: + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(name)s %(levelname)s %(message)s", + datefmt="%H:%M:%S", + ) + args = _arg_parser().parse_args() + model = _must_env("SWE_MODEL") + + # ── Load SWE-Gym tasks ──────────────────────────────────────── + gym_tasks = load_swegym_tasks(args.task_variant) + gym_tasks = gym_tasks[: args.max_tasks] + if not gym_tasks: + raise RuntimeError("No tasks loaded") + + _log.info("loaded %d SWE-Gym tasks", len(gym_tasks)) + + # ── Build dataset ───────────────────────────────────────────── + # Each row has "prompt" (chat messages) and "task_json" (full task + # for SWEToolEnv.reset). TRL passes all non-prompt columns to + # reset(**row) and to reward_func(**kwargs). + rows = [] + for gt in gym_tasks: + swe_task = gt.to_swe_task() + rows.append({ + "prompt": [{"role": "user", "content": swe_task.instruction}], + "task_json": json.dumps(gt.to_dict()), + "instance_id": gt.instance_id, + }) + dataset = Dataset.from_list(rows) + + # ── Sandbox backend ─────────────────────────────────────────── + backend = create_sandbox_backend(args.sandbox_backend) + + # ── Train ───────────────────────────────────────────────────── + trainer = AsyncGRPOTrainer( + model=model, + reward_funcs=swe_reward, + train_dataset=dataset, + environment_factory=SWEToolEnv.factory(backend), + args=AsyncGRPOConfig( + output_dir="outputs/swe_async_grpo", + vllm_server_base_url=args.vllm_url, + max_completion_length=2048, + max_tool_calling_iterations=args.max_turns, + max_steps=args.max_steps, + per_device_train_batch_size=1, + gradient_accumulation_steps=1, + num_generations=1, + learning_rate=1e-6, + temperature=1.0, + max_staleness=2, + weight_sync_steps=1, + max_inflight_tasks=2, + logging_steps=1, + log_completions=True, + num_completions_to_print=1, + report_to=[], + ), + ) + + _log.info( + "starting AsyncGRPO training: model=%s tasks=%d max_steps=%d backend=%s", + model, len(gym_tasks), args.max_steps, args.sandbox_backend, + ) + + trainer.train() + + _log.info("training complete: global_step=%s", getattr(trainer.state, "global_step", "?")) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 5a4e93de2325d83686a1c5be77a11690e4e2034d Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 19:11:57 +0530 Subject: [PATCH 34/79] feat: Async GRPO training with Pi agent and InterceptionServer --- envs/mini_swe_env/async_grpo/__init__.py | 35 +- envs/mini_swe_env/async_grpo/control_plane.py | 168 ++++++++ .../mini_swe_env/async_grpo/rollout_worker.py | 361 ++++++++++++++++++ envs/mini_swe_env/async_grpo/swe_tool_env.py | 289 -------------- examples/mini_swe_env/train_swe_async_grpo.py | 229 ++++++----- 5 files changed, 680 insertions(+), 402 deletions(-) create mode 100644 envs/mini_swe_env/async_grpo/control_plane.py create mode 100644 envs/mini_swe_env/async_grpo/rollout_worker.py delete mode 100644 envs/mini_swe_env/async_grpo/swe_tool_env.py diff --git a/envs/mini_swe_env/async_grpo/__init__.py b/envs/mini_swe_env/async_grpo/__init__.py index 2e84e8dfb..ae01b5912 100644 --- a/envs/mini_swe_env/async_grpo/__init__.py +++ b/envs/mini_swe_env/async_grpo/__init__.py @@ -1,24 +1,27 @@ -"""Async GRPO training for Mini SWE on HF Spaces. +"""Async GRPO training for Mini SWE with Pi agent. -This package provides: +Architecture: Pi runs in an HF Sandbox, its LLM calls are intercepted +by :class:`SWEAsyncControlPlane`, and :class:`SWERolloutWorker` forwards +them to vLLM ``/v1/completions`` to get exact token IDs and logprobs. -- :class:`SWEToolEnv` — TRL ``environment_factory``-compatible environment - with ``bash()`` and ``answer()`` tools backed by an HF/Docker sandbox. -- :func:`swe_reward` — Reward function that parses grading results from - completion messages. - -These plug directly into TRL's ``AsyncGRPOTrainer`` (or ``GRPOTrainer``). -No custom rollout worker, interception server, or control plane needed — -TRL handles tokenization, generation, logprobs, token IDs, weight sync, -and sample assembly. +The worker implements TRL's ``RolloutWorkerProtocol`` and plugs into +``AsyncGRPOTrainer(rollout_worker=...)``. """ -from .swe_tool_env import ( - SWEToolEnv, - swe_reward, +from .control_plane import ( + SWEAsyncControlPlane, + SWEAsyncControlPlaneConfig, +) +from .rollout_worker import ( + RolloutSample, + SWERolloutWorker, + WorkerConfig, ) __all__ = [ - "SWEToolEnv", - "swe_reward", + "RolloutSample", + "SWEAsyncControlPlane", + "SWEAsyncControlPlaneConfig", + "SWERolloutWorker", + "WorkerConfig", ] diff --git a/envs/mini_swe_env/async_grpo/control_plane.py b/envs/mini_swe_env/async_grpo/control_plane.py new file mode 100644 index 000000000..f6bd64a2d --- /dev/null +++ b/envs/mini_swe_env/async_grpo/control_plane.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +import asyncio +import contextlib +import logging +import os +from dataclasses import dataclass +from typing import Any, Mapping + +from openenv.core.harness.agents.interception_server import InterceptionServer + + +_log = logging.getLogger(__name__) + +_DEFAULT_INTERCEPTION_HOST = "0.0.0.0" +_DEFAULT_INTERCEPTION_PORT = 8765 + + +def build_hf_space_base_url(space_id: str) -> str: + """Return canonical public Space URL from ``owner/name``.""" + owner, sep, name = (space_id or "").strip().partition("/") + if not sep or not owner or not name: + raise ValueError( + "SPACE_ID must be in 'owner/name' format to derive interception base URL" + ) + return f"https://{owner}-{name}.hf.space" + + +@dataclass(frozen=True) +class SWEAsyncControlPlaneConfig: + """Runtime config for the Space-hosted interception control plane.""" + + host: str + port: int + auth_token: str + interception_base_url: str + + @classmethod + def from_env( + cls, + *, + env: Mapping[str, str] | None = None, + require_auth_token: bool = True, + ) -> "SWEAsyncControlPlaneConfig": + source = env or os.environ + + host = source.get("INTERCEPTION_HOST", _DEFAULT_INTERCEPTION_HOST).strip() + port_raw = source.get("INTERCEPTION_PORT") or source.get("PORT") + port = int(port_raw) if port_raw else _DEFAULT_INTERCEPTION_PORT + + token = ( + source.get("INTERCEPTION_AUTH_TOKEN") + or source.get("INTERCEPTION_SECRET") + or "" + ).strip() + if require_auth_token and not token: + raise ValueError( + "Missing interception auth token. Set INTERCEPTION_AUTH_TOKEN in Space secrets." + ) + + base_url = (source.get("INTERCEPTION_BASE_URL") or "").strip().rstrip("/") + if not base_url: + space_host = (source.get("SPACE_HOST") or "").strip().rstrip("/") + if space_host: + if "http" not in space_host: + space_host = f"https://{space_host}" + base_url = space_host.rstrip("/") + else: + space_id = (source.get("SPACE_ID") or "").strip() + if space_id: + base_url = build_hf_space_base_url(space_id) + + if not base_url: + raise ValueError( + "Could not resolve interception_base_url. Set INTERCEPTION_BASE_URL, " + "SPACE_HOST, or SPACE_ID." + ) + + return cls( + host=host, + port=port, + auth_token=token, + interception_base_url=base_url, + ) + + +class SWEAsyncControlPlane: + """Owns the interception server and rollout registration state. + + Trainer-side async rollout workers should use this object directly for + ``register_rollout/get_intercept/unregister_rollout`` so rollout queues and + intercept state remain in-process with the trainer. + """ + + def __init__( + self, + *, + config: SWEAsyncControlPlaneConfig, + server: InterceptionServer | None = None, + ) -> None: + self.config = config + self.server = server or InterceptionServer( + host=config.host, + port=config.port, + secret=config.auth_token, + ) + + @property + def interception_base_url(self) -> str: + return self.config.interception_base_url + + @property + def auth_token(self) -> str: + return self.config.auth_token + + async def start(self) -> None: + await self.server.start() + # If port was 0 in config, InterceptionServer resolves it after bind. + _log.info( + "swe_async_control_plane_started base_url=%s port=%d", + self.interception_base_url, + self.server.port, + ) + + async def stop(self) -> None: + await self.server.stop() + _log.info("swe_async_control_plane_stopped") + + def register_rollout( + self, + rollout_id: str, + *, + state: dict[str, Any] | None = None, + ) -> asyncio.Queue: + queue = self.server.register_rollout(rollout_id, state=state) + stats = self.stats() + _log.info( + "swe_async_rollout_registered rollout_id=%s active_rollouts=%d", + rollout_id, + stats["active_rollouts"], + ) + return queue + + def unregister_rollout(self, rollout_id: str) -> None: + self.server.unregister_rollout(rollout_id) + stats = self.stats() + _log.info( + "swe_async_rollout_unregistered rollout_id=%s active_rollouts=%d", + rollout_id, + stats["active_rollouts"], + ) + + @contextlib.contextmanager + def rollout( + self, + rollout_id: str, + *, + state: dict[str, Any] | None = None, + ): + """Leak-safe context manager for one rollout registration.""" + queue = self.register_rollout(rollout_id, state=state) + try: + yield queue + finally: + self.unregister_rollout(rollout_id) + + def stats(self) -> dict[str, int]: + return self.server.stats() diff --git a/envs/mini_swe_env/async_grpo/rollout_worker.py b/envs/mini_swe_env/async_grpo/rollout_worker.py new file mode 100644 index 000000000..d00ca94cf --- /dev/null +++ b/envs/mini_swe_env/async_grpo/rollout_worker.py @@ -0,0 +1,361 @@ +"""Custom rollout worker for Pi-in-sandbox SWE training. + +Implements ``RolloutWorkerProtocol`` from TRL's ``AsyncGRPOTrainer``. + +Architecture: + Pi (sandbox) → InterceptionServer → this worker → vLLM /v1/completions + ← chat response back to Pi + +Pi drives the generation loop inside the sandbox. This worker: + +1. Dequeues each intercepted LLM request from Pi. +2. Tokenizes the messages with ``apply_chat_template``. +3. Calls vLLM ``/v1/completions`` with ``prompt=token_ids``, + ``return_token_ids=True``, ``logprobs=0`` — same as TRL's own + ``AsyncRolloutWorker._generate_one_turn``. +4. Gets exact ``completion_ids`` and ``completion_logprobs`` from vLLM. +5. Wraps the completion text as a chat response and delivers it back to Pi. +6. Tracks multi-turn token sequences matching TRL's pattern: + ``input_ids = initial_prompt_ids + [turn_ids + suffix_ids]*N`` + ``completion_mask = [0]*prompt + [1]*turn + [0]*suffix + ...`` +7. On ``answer()``, bridges to host-side grading. +8. Assembles the final ``RolloutSample`` and pushes to ``rollout_buffer``. +""" + +from __future__ import annotations + +import asyncio +import logging +import queue +import threading +import time +import uuid +from dataclasses import dataclass, field +from typing import Any, Iterator, Sequence, cast + +import requests + +from ..models import SWETask + +_log = logging.getLogger(__name__) + + +# ── Sample dataclass ─────────────────────────────────────────────────── + +@dataclass +class RolloutSample: + """Matches the fields TRL's ``RolloutQueueDataset`` reads.""" + + input_ids: list[int] + completion_mask: list[int] + old_log_probs: list[float] + advantage: float + model_version: int + metrics: dict[str, Any] = field(default_factory=dict) + + +# ── Config ───────────────────────────────────────────────────────────── + +@dataclass(frozen=True) +class WorkerConfig: + max_inflight: int = 2 + queue_maxsize: int = 64 + request_timeout_s: float = 600.0 + max_turns: int = 50 + max_completion_tokens: int = 2048 + temperature: float = 1.0 + idle_backoff_s: float = 0.5 + + +# ── Worker ───────────────────────────────────────────────────────────── + +class SWERolloutWorker: + """Background rollout producer for Pi + InterceptionServer + vLLM. + + Implements ``RolloutWorkerProtocol`` so it plugs into + ``AsyncGRPOTrainer(rollout_worker=...)``. + """ + + def __init__( + self, + *, + session_factory: Any, + tasks: Sequence[SWETask], + tokenizer: Any, + vllm_base_url: str, + vllm_api_key: str, + vllm_model: str, + config: WorkerConfig | None = None, + ) -> None: + self._factory = session_factory + self._tasks = list(tasks) + self._tokenizer = tokenizer + self._vllm_base_url = vllm_base_url.rstrip("/") + self._vllm_api_key = vllm_api_key + self._vllm_model = vllm_model + self._cfg = config or WorkerConfig() + + self.rollout_buffer: queue.Queue[RolloutSample] = queue.Queue( + maxsize=self._cfg.queue_maxsize, + ) + + self._stop = threading.Event() + self._pause = threading.Event() + self._lock = threading.Lock() + self._threads: list[threading.Thread] = [] + self._task_idx = 0 + self._model_version = 0 + self._started = False + + # ── RolloutWorkerProtocol ────────────────────────────────────── + + def start(self) -> None: + with self._lock: + if self._started: + return + self._started = True + self._stop.clear() + for i in range(max(1, self._cfg.max_inflight)): + t = threading.Thread( + target=self._loop, args=(i,), daemon=True, + name=f"swe-rollout-{i}", + ) + t.start() + self._threads.append(t) + _log.info("worker started threads=%d", len(self._threads)) + + def stop(self) -> None: + self._stop.set() + for t in self._threads: + t.join(timeout=5.0) + with self._lock: + self._started = False + self._threads = [] + + def pause(self) -> None: + self._pause.set() + + def resume(self) -> None: + self._pause.clear() + + def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: + # Consume iterator (required by protocol). Real NCCL sync is future work. + for _ in iterator: + pass + + def update_model_version(self, version: int) -> None: + with self._lock: + self._model_version = version + + # ── Internal ─────────────────────────────────────────────────── + + def _next_task(self) -> SWETask: + with self._lock: + t = self._tasks[self._task_idx % len(self._tasks)] + self._task_idx += 1 + return t + + def _model_ver(self) -> int: + with self._lock: + return self._model_version + + def _loop(self, idx: int) -> None: + while not self._stop.is_set(): + while self._pause.is_set() and not self._stop.is_set(): + time.sleep(0.05) + if self._stop.is_set(): + return + + task = self._next_task() + eid = f"swe-{idx}-{uuid.uuid4().hex[:8]}" + try: + sample = asyncio.run(self._rollout(task, eid)) + except Exception: + _log.exception("rollout failed worker=%d id=%s", idx, task.instance_id) + time.sleep(self._cfg.idle_backoff_s) + continue + + if sample is None: + time.sleep(self._cfg.idle_backoff_s) + continue + + try: + self.rollout_buffer.put(sample, timeout=2.0) + except queue.Full: + _log.warning("queue full, dropping %s", task.instance_id) + + # ── Single rollout ───────────────────────────────────────────── + + async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None: + session = self._factory.create(task=task, episode_id=episode_id) + + # Accumulate the full token sequence across turns, matching TRL's + # _generate_one pattern: + # input_ids = initial_prompt_ids + turn1_ids + suffix1_ids + turn2_ids + ... + # completion_mask = [0]*prompt + [1]*turn1 + [0]*suffix1 + [1]*turn2 + ... + # old_log_probs = [0.0]*prompt + lp1 + [0.0]*suf1 + lp2 + ... + all_ids: list[int] = [] + all_mask: list[int] = [] + all_lps: list[float] = [] + + initial_prompt_ids: list[int] | None = None + prev_prompt_ids: list[int] | None = None + + turns = 0 + answer_called = False + t0 = time.time() + + try: + while turns < self._cfg.max_turns and not self._stop.is_set(): + intercept = await session.next_request( + timeout_s=self._cfg.request_timeout_s, + ) + if intercept is None: + break + + # ── Tokenize this turn's full prompt ────────────── + messages = _get_messages(intercept) + current_prompt_ids = self._tokenizer.apply_chat_template( + messages, + add_generation_prompt=True, + return_dict=False, + ) + + if initial_prompt_ids is None: + # First turn: the entire prompt is non-completion tokens. + initial_prompt_ids = current_prompt_ids + all_ids.extend(current_prompt_ids) + all_mask.extend([0] * len(current_prompt_ids)) + all_lps.extend([0.0] * len(current_prompt_ids)) + elif prev_prompt_ids is not None: + # Subsequent turns: the delta between prev generation end + # and this turn's prompt is the tool-result suffix. + # prev_prompt_ids + prev_turn_ids = end of last generation + # current_prompt_ids = prev_prompt_ids + prev_turn_ids + suffix_ids + prev_len = len(prev_prompt_ids) + suffix_ids = current_prompt_ids[prev_len:] + all_ids.extend(suffix_ids) + all_mask.extend([0] * len(suffix_ids)) + all_lps.extend([0.0] * len(suffix_ids)) + + turns += 1 + + # ── Generate via /v1/completions ────────────────── + turn_ids, turn_lps, text = self._generate(current_prompt_ids) + + all_ids.extend(turn_ids) + all_mask.extend([1] * len(turn_ids)) + all_lps.extend(turn_lps) + + # For next turn's suffix computation: + prev_prompt_ids = current_prompt_ids + turn_ids + + # ── Build chat response for Pi ──────────────────── + chat_resp = _make_chat_response(text, self._vllm_model) + + # ── Check for answer tool call ──────────────────── + if _has_answer_call(chat_resp): + answer_called = True + + await session.deliver(intercept, chat_resp) + + if answer_called: + break + + # ── Reward ──────────────────────────────────────────── + vr = session.verify(transcript=[]) + reward = float(getattr(vr, "env_reward", 0.0) or 0.0) + metrics = dict(getattr(vr, "metrics", {}) or {}) + + if not all_lps: + pad = getattr(self._tokenizer, "pad_token_id", 0) or 0 + all_ids = [pad] + all_mask = [1] + all_lps = [0.0] + + return RolloutSample( + input_ids=all_ids, + completion_mask=all_mask, + old_log_probs=all_lps, + advantage=reward, + model_version=self._model_ver(), + metrics={ + **metrics, + "reward": reward, + "instance_id": task.instance_id, + "turns": turns, + "answer_called": answer_called, + "wall_s": round(time.time() - t0, 3), + "n_tokens": len(all_ids), + }, + ) + finally: + session.close() + + # ── vLLM call (matches TRL's _generate_one_turn exactly) ────── + + def _generate( + self, prompt_ids: list[int], + ) -> tuple[list[int], list[float], str]: + """POST /v1/completions with token IDs. Returns (ids, logprobs, text).""" + body = { + "model": self._vllm_model, + "prompt": prompt_ids, + "max_tokens": self._cfg.max_completion_tokens, + "temperature": self._cfg.temperature, + "n": 1, + "return_token_ids": True, + "logprobs": 0, + } + resp = requests.post( + f"{self._vllm_base_url}/v1/completions", + headers={ + "Authorization": f"Bearer {self._vllm_api_key}", + "Content-Type": "application/json", + }, + json=body, + timeout=self._cfg.request_timeout_s, + ) + if resp.status_code != 200: + raise RuntimeError(f"vllm {resp.status_code}: {resp.text[:400]}") + + choice = resp.json()["choices"][0] + return ( + choice["token_ids"], + choice["logprobs"]["token_logprobs"], + choice.get("text", ""), + ) + + +# ── Helpers ──────────────────────────────────────────────────────────── + +def _get_messages(intercept: dict[str, Any]) -> list[dict[str, Any]]: + msgs = intercept.get("messages") + if isinstance(msgs, list) and msgs: + return msgs + body = intercept.get("body") or {} + msgs = body.get("messages") + if isinstance(msgs, list) and msgs: + return msgs + raise RuntimeError("intercept has no messages") + + +def _make_chat_response(text: str, model: str) -> dict[str, Any]: + return { + "id": f"chatcmpl-{uuid.uuid4().hex[:8]}", + "object": "chat.completion", + "model": model, + "choices": [{ + "index": 0, + "message": {"role": "assistant", "content": text}, + "finish_reason": "stop", + }], + } + + +def _has_answer_call(resp: dict[str, Any]) -> bool: + for choice in (resp.get("choices") or []): + for tc in ((choice or {}).get("message") or {}).get("tool_calls") or []: + if ((tc or {}).get("function") or {}).get("name") == "answer": + return True + return False diff --git a/envs/mini_swe_env/async_grpo/swe_tool_env.py b/envs/mini_swe_env/async_grpo/swe_tool_env.py deleted file mode 100644 index 7ea5fc99e..000000000 --- a/envs/mini_swe_env/async_grpo/swe_tool_env.py +++ /dev/null @@ -1,289 +0,0 @@ -"""SWE tool environment for TRL AsyncGRPO / GRPO training. - -Implements the ``environment_factory`` protocol expected by -``AsyncGRPOTrainer`` and ``GRPOTrainer``: - -- ``reset(**row)`` — create a sandbox from the SWE-Gym per-task image. -- ``bash(command)`` — execute a shell command in ``/testbed``. -- ``answer()`` — run SWE-Gym grading and return resolved/not. - -TRL discovers public methods as tools, drives the model's generation -loop, handles tokenization, logprobs, token IDs, weight sync, and -sample assembly. We only provide the environment. - -Usage with ``AsyncGRPOTrainer``:: - - from mini_swe_env.async_grpo import SWEToolEnv, swe_reward - - trainer = AsyncGRPOTrainer( - model="Qwen/Qwen3-1.7B", - reward_funcs=swe_reward, - train_dataset=dataset, - environment_factory=SWEToolEnv.factory(backend), - ) - trainer.train() -""" - -from __future__ import annotations - -import json -import logging -import shlex -from typing import Any, Callable - -from ..grading import grade_from_case_results -from ..models import SWEGymTask, SWETask - -_log = logging.getLogger(__name__) - -TESTBED = "/testbed" -VERIFY_TIMEOUT_S = 300 - - -class SWEToolEnv: - """TRL-compatible SWE environment with ``bash`` and ``answer`` tools. - - One instance is created per ``max_inflight_tasks`` slot. TRL calls - ``reset(**row)`` before each generation, then the model calls - ``bash(command=...)`` and ``answer()`` as tool calls. - """ - - def __init__(self, sandbox_backend: Any) -> None: - self._backend = sandbox_backend - self._sandbox: Any | None = None - self._task: SWETask | None = None - self._gym_task: SWEGymTask | None = None - self.reward: float = 0.0 - self.resolved: bool = False - self._answer_called: bool = False - - # ── TRL lifecycle ────────────────────────────────────────────── - - def reset(self, **kwargs: Any) -> str | None: - """Create a fresh sandbox for the task described in *kwargs*. - - Called by TRL before each generation. Receives all columns from - the dataset row as keyword arguments. - """ - self._cleanup() - self.reward = 0.0 - self.resolved = False - self._answer_called = False - - task_json = kwargs.get("task_json", "") - if not task_json: - return None - - raw = json.loads(task_json) - self._gym_task = SWEGymTask(**raw) - self._task = self._gym_task.to_swe_task() - - self._sandbox = self._backend.create( - timeout_s=self._task.timeout_s, - image=self._task.sandbox_image, - ) - - return None - - # ── Tools (discovered by TRL via inspect.getmembers) ────────── - - def bash(self, command: str) -> str: - """Execute a shell command in the repository at /testbed. - - Args: - command: Shell command to run. - - Returns: - Combined stdout and stderr output. - """ - if self._sandbox is None: - return "Error: no sandbox — call reset() first." - result = self._sandbox.exec(command, cwd=TESTBED, timeout=VERIFY_TIMEOUT_S) - stdout = (result.stdout or "")[-8000:] - stderr = (result.stderr or "")[-4000:] - if stderr: - return f"{stdout}\nSTDERR:\n{stderr}" - return stdout - - def answer(self) -> str: - """Submit your solution for grading. - - Runs the SWE-Gym test suite (FAIL_TO_PASS / PASS_TO_PASS) and - returns whether the issue is resolved. Can only be called once. - - Returns: - A string indicating whether the issue was resolved. - """ - if self._answer_called: - return "Error: answer() already called." - self._answer_called = True - - if self._sandbox is None or self._gym_task is None: - self.reward = 0.0 - self.resolved = False - return "Error: no sandbox or task — call reset() first." - - try: - reward, resolved = _grade_submission( - self._sandbox, self._task, self._gym_task, - ) - self.reward = reward - self.resolved = resolved - return f"Resolved: {str(resolved).lower()}" - except Exception as exc: - _log.exception("SWE grading failed for %s", self._gym_task.instance_id) - self.reward = 0.0 - self.resolved = False - return f"Grading error: {type(exc).__name__}: {exc}" - - # ── Cleanup ──────────────────────────────────────────────────── - - def _cleanup(self) -> None: - if self._sandbox is not None: - try: - self._sandbox.kill() - except Exception: - pass - self._sandbox = None - self._task = None - self._gym_task = None - - def __del__(self) -> None: - self._cleanup() - - # ── Factory helper ───────────────────────────────────────────── - - @staticmethod - def factory(sandbox_backend: Any) -> Callable[[], "SWEToolEnv"]: - """Return an ``environment_factory`` callable for TRL.""" - def _create() -> SWEToolEnv: - return SWEToolEnv(sandbox_backend) - return _create - - -# ── Reward function ──────────────────────────────────────────────────── - - -def swe_reward( - completions: list[Any], - **kwargs: Any, -) -> list[float]: - """Reward function for SWE training. - - Scans each completion for an ``answer`` tool result message and - parses ``"Resolved: true"`` / ``"Resolved: false"``. - - Works with both ``GRPOTrainer`` (receives ``environments=``) and - ``AsyncGRPOTrainer`` (does not — parses completion messages). - """ - # If GRPOTrainer passes environments directly, use them. - environments = kwargs.get("environments") - if environments is not None: - return [float(getattr(env, "reward", 0.0)) for env in environments] - - # AsyncGRPOTrainer path: parse completion messages. - rewards: list[float] = [] - for completion in completions: - reward = 0.0 - if isinstance(completion, list): - for msg in completion: - if not isinstance(msg, dict): - continue - if msg.get("role") == "tool" and msg.get("name") == "answer": - content = str(msg.get("content", "")) - if "resolved: true" in content.lower(): - reward = 1.0 - break - rewards.append(reward) - return rewards - - -# ── Grading (extracted from harness.py) ──────────────────────────────── - - -def _grade_submission( - sandbox: Any, - task: SWETask, - gym_task: SWEGymTask, -) -> tuple[float, bool]: - """Run SWE-Gym grading: revert test files, apply test_patch, run tests.""" - touched_files = _extract_paths_from_test_patch(gym_task.test_patch) - - # Revert test files to base_commit state before applying test_patch. - _revert_test_files(sandbox, base_commit=task.base_commit, paths=touched_files) - - # Apply the test patch. - patch_path = "/tmp/.openenv_swe_test_patch.diff" - sandbox.write_text(patch_path, gym_task.test_patch) - result = sandbox.exec( - f"git apply --whitespace=nowarn {shlex.quote(patch_path)}", - cwd=TESTBED, - timeout=30, - ) - if result.exit_code != 0: - raise RuntimeError( - f"failed to apply test_patch: {(result.stderr or result.stdout or '').strip()}" - ) - - # Run each test case. - cases: list[str] = [] - seen: set[str] = set() - for case in [*gym_task.FAIL_TO_PASS, *gym_task.PASS_TO_PASS]: - if case not in seen: - seen.add(case) - cases.append(case) - - case_results: dict[str, bool] = {} - for case in cases: - cmd = f"python -m pytest -q --maxfail=1 {shlex.quote(case)}" - run = sandbox.exec(cmd, cwd=TESTBED, timeout=VERIFY_TIMEOUT_S) - case_results[case] = run.exit_code == 0 - - grade = grade_from_case_results(gym_task, case_results) - - # Best-effort cleanup. - try: - _revert_test_files(sandbox, base_commit=task.base_commit, paths=touched_files) - except Exception: - pass - - return float(grade.reward), bool(grade.resolved) - - -def _extract_paths_from_test_patch(test_patch: str) -> list[str]: - paths: list[str] = [] - for line in (test_patch or "").splitlines(): - if line.startswith("+++ b/"): - path = line[len("+++ b/"):].strip() - if path and path != "/dev/null": - paths.append(path) - return sorted(set(paths)) - - -def _revert_test_files( - sandbox: Any, - *, - base_commit: str, - paths: list[str], -) -> None: - for path in paths: - has_file = sandbox.exec( - f"git cat-file -e {shlex.quote(f'{base_commit}:{path}')}", - cwd=TESTBED, - timeout=10, - ) - if has_file.exit_code == 0: - cmd = f"git checkout --quiet {shlex.quote(base_commit)} -- {shlex.quote(path)}" - else: - cmd = f"rm -f -- {shlex.quote(path)}" - result = sandbox.exec(cmd, cwd=TESTBED, timeout=20) - if result.exit_code != 0: - raise RuntimeError( - f"failed to revert {path}: {(result.stderr or result.stdout or '').strip()}" - ) - - -__all__ = [ - "SWEToolEnv", - "swe_reward", -] diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 75763c035..29d8753ae 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -1,37 +1,28 @@ #!/usr/bin/env python3 -"""Train SWE with TRL AsyncGRPOTrainer + environment_factory. +"""Train SWE with AsyncGRPOTrainer + Pi agent + InterceptionServer. -Uses TRL's built-in AsyncRolloutWorker which handles: -- Tokenization via apply_chat_template -- Generation via /v1/completions with exact token_ids + logprobs -- Multi-turn tool calling (bash, answer) -- NCCL weight sync to vLLM -- Sample assembly (prompt_ids + completion_ids) +Architecture: + Pi (HF Sandbox) → InterceptionServer → SWERolloutWorker → vLLM /v1/completions + ← chat response back to Pi -We only provide: -- SWEToolEnv: environment with bash() and answer() tools -- swe_reward: parses completion messages for grading result -- Dataset: SWE-Gym tasks as prompts +vLLM runs on a separate GPU. The trainer, interception server, and rollout +worker share a process on the training GPU. Prerequisites: - - vLLM server running on a separate GPU - - HF Sandbox or Docker backend available - -Example: - # GPU 0: vLLM - CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-1.7B \\ - --tensor-parallel-size 1 --max-model-len 4096 - - # GPU 1: Trainer - CUDA_VISIBLE_DEVICES=1 \\ - SWE_MODEL=Qwen/Qwen3-1.7B \\ - PYTHONPATH=src:envs python examples/mini_swe_env/train_swe_async_grpo.py \\ - --task-variant lite --max-tasks 5 --max-steps 10 + CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-1.7B \\ + --tensor-parallel-size 1 --max-model-len 4096 + + CUDA_VISIBLE_DEVICES=1 \\ + SWE_MODEL=Qwen/Qwen3-1.7B \\ + INTERCEPTION_AUTH_TOKEN=secret123 \\ + INTERCEPTION_BASE_URL=http://localhost:8765 \\ + PYTHONPATH=src:envs python examples/mini_swe_env/train_swe_async_grpo.py """ from __future__ import annotations import argparse +import asyncio import json import logging import os @@ -44,9 +35,18 @@ sys.path.insert(0, str(_p)) from datasets import Dataset # noqa: E402 +from transformers import AutoTokenizer # noqa: E402 from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer # noqa: E402 -from mini_swe_env.async_grpo.swe_tool_env import SWEToolEnv, swe_reward # noqa: E402 +from mini_swe_env.async_grpo.control_plane import ( # noqa: E402 + SWEAsyncControlPlane, + SWEAsyncControlPlaneConfig, +) +from mini_swe_env.async_grpo.rollout_worker import ( # noqa: E402 + SWERolloutWorker, + WorkerConfig, +) +from mini_swe_env.harness import SWEAgentConfig, SWESessionFactory # noqa: E402 from mini_swe_env.task_loader_swegym import load_swegym_tasks # noqa: E402 from openenv.core.harness.sandbox import create_sandbox_backend # noqa: E402 @@ -54,22 +54,23 @@ _log = logging.getLogger("swe-async-grpo") -def _arg_parser() -> argparse.ArgumentParser: - p = argparse.ArgumentParser(description="Train SWE with TRL AsyncGRPO") +def _args() -> argparse.Namespace: + p = argparse.ArgumentParser() p.add_argument("--task-variant", default="lite", choices=["lite", "full"]) p.add_argument("--max-tasks", type=int, default=5) p.add_argument("--max-steps", type=int, default=10) p.add_argument("--max-turns", type=int, default=30) p.add_argument("--sandbox-backend", default="hf", choices=["docker", "e2b", "hf"]) p.add_argument("--vllm-url", default="http://localhost:8000") - return p + p.add_argument("--agent", default="pi", choices=["pi", "opencode"]) + return p.parse_args() -def _must_env(name: str) -> str: - value = os.environ.get(name, "").strip() - if not value: - raise RuntimeError(f"Missing required env var: {name}") - return value +def _env(name: str) -> str: + v = os.environ.get(name, "").strip() + if not v: + raise RuntimeError(f"Missing env var: {name}") + return v def main() -> int: @@ -78,71 +79,105 @@ def main() -> int: format="%(asctime)s %(name)s %(levelname)s %(message)s", datefmt="%H:%M:%S", ) - args = _arg_parser().parse_args() - model = _must_env("SWE_MODEL") - - # ── Load SWE-Gym tasks ──────────────────────────────────────── - gym_tasks = load_swegym_tasks(args.task_variant) - gym_tasks = gym_tasks[: args.max_tasks] - if not gym_tasks: - raise RuntimeError("No tasks loaded") - - _log.info("loaded %d SWE-Gym tasks", len(gym_tasks)) - - # ── Build dataset ───────────────────────────────────────────── - # Each row has "prompt" (chat messages) and "task_json" (full task - # for SWEToolEnv.reset). TRL passes all non-prompt columns to - # reset(**row) and to reward_func(**kwargs). - rows = [] - for gt in gym_tasks: - swe_task = gt.to_swe_task() - rows.append({ - "prompt": [{"role": "user", "content": swe_task.instruction}], - "task_json": json.dumps(gt.to_dict()), - "instance_id": gt.instance_id, - }) - dataset = Dataset.from_list(rows) - - # ── Sandbox backend ─────────────────────────────────────────── - backend = create_sandbox_backend(args.sandbox_backend) - - # ── Train ───────────────────────────────────────────────────── - trainer = AsyncGRPOTrainer( - model=model, - reward_funcs=swe_reward, - train_dataset=dataset, - environment_factory=SWEToolEnv.factory(backend), - args=AsyncGRPOConfig( - output_dir="outputs/swe_async_grpo", - vllm_server_base_url=args.vllm_url, - max_completion_length=2048, - max_tool_calling_iterations=args.max_turns, - max_steps=args.max_steps, - per_device_train_batch_size=1, - gradient_accumulation_steps=1, - num_generations=1, - learning_rate=1e-6, - temperature=1.0, - max_staleness=2, - weight_sync_steps=1, - max_inflight_tasks=2, - logging_steps=1, - log_completions=True, - num_completions_to_print=1, - report_to=[], - ), - ) - - _log.info( - "starting AsyncGRPO training: model=%s tasks=%d max_steps=%d backend=%s", - model, len(gym_tasks), args.max_steps, args.sandbox_backend, - ) - - trainer.train() - - _log.info("training complete: global_step=%s", getattr(trainer.state, "global_step", "?")) - return 0 + args = _args() + model = _env("SWE_MODEL") + vllm_url = args.vllm_url + vllm_key = os.environ.get("VLLM_API_KEY", "token").strip() + + # ── Load tasks ──────────────────────────────────────────────── + gym_tasks = load_swegym_tasks(args.task_variant)[: args.max_tasks] + swe_tasks = [t.to_swe_task() for t in gym_tasks] + _log.info("loaded %d tasks", len(swe_tasks)) + + # ── Dataset (prompt per task) ───────────────────────────────── + dataset = Dataset.from_list([ + { + "prompt": [{"role": "user", "content": t.instruction}], + "instance_id": t.instance_id, + } + for t in swe_tasks + ]) + + # ── Tokenizer ───────────────────────────────────────────────── + tokenizer = AutoTokenizer.from_pretrained(model) + if tokenizer.pad_token is None: + tokenizer.pad_token = tokenizer.eos_token + + # ── Interception control plane ──────────────────────────────── + control_cfg = SWEAsyncControlPlaneConfig.from_env() + control_plane = SWEAsyncControlPlane(config=control_cfg) + asyncio.run(control_plane.start()) + + try: + # ── Session factory (Pi in sandbox) ─────────────────────── + backend = create_sandbox_backend(args.sandbox_backend) + session_factory = SWESessionFactory( + agent=args.agent, + config=SWEAgentConfig( + base_url=control_plane.interception_base_url, + api_key=control_plane.auth_token, + model=model, + agent_timeout_s=1800.0, + ), + sandbox_backend=backend, + mode="interception_gate", + interception_server=control_plane.server, + interception_base_url=control_plane.interception_base_url, + ) + + # ── Rollout worker ──────────────────────────────────────── + worker = SWERolloutWorker( + session_factory=session_factory, + tasks=swe_tasks, + tokenizer=tokenizer, + vllm_base_url=vllm_url, + vllm_api_key=vllm_key, + vllm_model=model, + config=WorkerConfig( + max_inflight=2, + max_turns=args.max_turns, + ), + ) + + # ── Trainer ─────────────────────────────────────────────── + def _noop_reward(**kwargs: Any) -> list[float]: # noqa: ANN401 + """Unused — rewards come from rollout_worker.advantage.""" + prompts = kwargs.get("prompts", []) + return [0.0] * len(prompts) + + trainer = AsyncGRPOTrainer( + model=model, + reward_funcs=_noop_reward, + train_dataset=dataset, + processing_class=tokenizer, + rollout_worker=worker, + args=AsyncGRPOConfig( + output_dir="outputs/swe_async_grpo", + vllm_server_base_url=vllm_url, + vllm_server_timeout=2400.0, + max_completion_length=2048, + max_steps=args.max_steps, + per_device_train_batch_size=1, + gradient_accumulation_steps=1, + num_generations=1, + learning_rate=1e-6, + temperature=1.0, + max_staleness=4, + weight_sync_steps=1, + max_inflight_tasks=2, + logging_steps=1, + report_to=[], + ), + ) + + _log.info("starting training: model=%s tasks=%d", model, len(swe_tasks)) + trainer.train() + _log.info("done: step=%s", getattr(trainer.state, "global_step", "?")) + return 0 + finally: + asyncio.run(control_plane.stop()) if __name__ == "__main__": + from typing import Any # noqa: E402 raise SystemExit(main()) From 65023623df2bbeed2b75f97be1416bb18b2f1d3c Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 19:55:31 +0530 Subject: [PATCH 35/79] chore: use trackio --- examples/mini_swe_env/train_swe_async_grpo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 29d8753ae..223c2e0ae 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -166,7 +166,9 @@ def _noop_reward(**kwargs: Any) -> list[float]: # noqa: ANN401 weight_sync_steps=1, max_inflight_tasks=2, logging_steps=1, - report_to=[], + report_to="trackio", + run_name=f"swe-grpo-{model.split('/')[-1]}", + trackio_space_id=os.environ.get("TRACKIO_SPACE_ID", "").strip() or None, ), ) From 6e59c9602ff47f1430c945af17104f2039dcbd32 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 22:26:22 +0530 Subject: [PATCH 36/79] feat: Dockerfile, README, and start script --- .../mini_swe_env/async_grpo/rollout_worker.py | 8 +- .../async_grpo/space_app/Dockerfile | 39 + .../async_grpo/space_app/README.md | 34 + .../async_grpo/space_app/start.sh | 77 + envs/mini_swe_env/pyproject.toml | 49 + envs/mini_swe_env/uv.lock | 4806 +++++++++++++++++ 6 files changed, 5008 insertions(+), 5 deletions(-) create mode 100644 envs/mini_swe_env/async_grpo/space_app/Dockerfile create mode 100644 envs/mini_swe_env/async_grpo/space_app/README.md create mode 100755 envs/mini_swe_env/async_grpo/space_app/start.sh create mode 100644 envs/mini_swe_env/pyproject.toml create mode 100644 envs/mini_swe_env/uv.lock diff --git a/envs/mini_swe_env/async_grpo/rollout_worker.py b/envs/mini_swe_env/async_grpo/rollout_worker.py index d00ca94cf..ba50bfe30 100644 --- a/envs/mini_swe_env/async_grpo/rollout_worker.py +++ b/envs/mini_swe_env/async_grpo/rollout_worker.py @@ -280,13 +280,11 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None advantage=reward, model_version=self._model_ver(), metrics={ - **metrics, "reward": reward, - "instance_id": task.instance_id, - "turns": turns, - "answer_called": answer_called, + "turns": float(turns), + "answer_called": float(answer_called), "wall_s": round(time.time() - t0, 3), - "n_tokens": len(all_ids), + "n_tokens": float(len(all_ids)), }, ) finally: diff --git a/envs/mini_swe_env/async_grpo/space_app/Dockerfile b/envs/mini_swe_env/async_grpo/space_app/Dockerfile new file mode 100644 index 000000000..a995f11a6 --- /dev/null +++ b/envs/mini_swe_env/async_grpo/space_app/Dockerfile @@ -0,0 +1,39 @@ +FROM vllm/vllm-openai:latest + +# System deps + Docker CLI (for Docker sandbox backend) +RUN apt-get update -qq && \ + apt-get install -y -qq --no-install-recommends curl ca-certificates docker.io && \ + update-ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# uv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + +# HF Spaces requires uid 1000 +RUN useradd -m -u 1000 user && usermod -aG docker user +ENV HOME=/home/user \ + PATH=/home/user/.local/bin:$PATH + +# Pre-create HF cache dir (writable by user) +RUN mkdir -p /home/user/.cache/huggingface && chown -R user:user /home/user/.cache + +# Copy repo +WORKDIR $HOME/app +COPY --chown=user . . + +# Install: openenv-core + mini_swe_env[train] into system Python +RUN uv pip install --system --no-cache \ + -e "." \ + -e "envs/mini_swe_env[train]" + +# Runtime defaults +ENV PYTHONPATH=$HOME/app/src:$HOME/app/envs \ + PYTHONUNBUFFERED=1 \ + INTERCEPTION_HOST=0.0.0.0 \ + INTERCEPTION_PORT=7860 + +USER user +EXPOSE 7860 + +ENTRYPOINT [] +CMD ["bash", "envs/mini_swe_env/async_grpo/space_app/start.sh"] diff --git a/envs/mini_swe_env/async_grpo/space_app/README.md b/envs/mini_swe_env/async_grpo/space_app/README.md new file mode 100644 index 000000000..1fa5b8186 --- /dev/null +++ b/envs/mini_swe_env/async_grpo/space_app/README.md @@ -0,0 +1,34 @@ +--- +title: SWE Async GRPO Training +emoji: 🔧 +colorFrom: blue +colorTo: green +sdk: docker +app_port: 7860 +suggested_hardware: a10g-largex2 +startup_duration_timeout: 30m +preload_from_hub: + - Qwen/Qwen3-1.7B +--- + +# SWE Async GRPO Training + +Trains a language model on SWE-Gym tasks using TRL's AsyncGRPOTrainer +with Pi as the coding agent. + +**Architecture**: Pi runs in HF Sandboxes, its LLM calls are intercepted +and forwarded to a co-located vLLM server for generation with exact +token IDs and logprobs. The trainer runs GRPO updates on a second GPU. + +See `SWE_ASYNC_GRPO_SPACE_DEPLOYMENT.md` in the repo for full details. + +## Required Secrets + +Set these in the Space Settings tab: + +| Secret | Description | +|--------|-------------| +| `HF_TOKEN` | HF token for sandbox creation and model downloads | +| `INTERCEPTION_AUTH_TOKEN` | Shared auth token for Pi ↔ InterceptionServer | +| `SWE_MODEL` | Model ID to serve and train (e.g. `Qwen/Qwen3-1.7B`) | +| `TRACKIO_SPACE_ID` | Trackio dashboard Space for metrics (e.g. `user/swe-grpo-dashboard`) | diff --git a/envs/mini_swe_env/async_grpo/space_app/start.sh b/envs/mini_swe_env/async_grpo/space_app/start.sh new file mode 100755 index 000000000..c2f5fbcee --- /dev/null +++ b/envs/mini_swe_env/async_grpo/space_app/start.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# Startup script for SWE Async GRPO training. +# +# Used both in HF Spaces and local Docker testing. +# +# Process 1 (background): vLLM server +# Process 2 (foreground): Trainer + InterceptionServer +# +# HF Spaces monitors app_port (7860) for health. The InterceptionServer +# binds to 7860 and serves /health, so the Space is marked Running once +# the trainer starts. +set -e + +MODEL="${SWE_MODEL:?ERROR: Set SWE_MODEL (e.g. Qwen/Qwen3-1.7B)}" +VLLM_PORT="${VLLM_PORT:-8000}" +VLLM_KEY="${VLLM_API_KEY:-token}" +MAX_MODEL_LEN="${MAX_MODEL_LEN:-4096}" +GPU_MEM_UTIL="${GPU_MEMORY_UTILIZATION:-0.9}" + +# GPU assignment. On 2-GPU Spaces: vLLM=0, trainer=1. +# On single GPU: both empty (share GPU 0). +VLLM_GPU="${VLLM_GPU:-0}" +TRAINER_GPU="${TRAINER_GPU:-1}" + +echo "========================================" +echo "SWE Async GRPO Training" +echo "Model: $MODEL" +echo "vLLM port: $VLLM_PORT" +echo "Max model len: $MAX_MODEL_LEN" +echo "GPU mem util: $GPU_MEM_UTIL" +echo "vLLM GPU: $VLLM_GPU" +echo "Trainer GPU: $TRAINER_GPU" +echo "========================================" + +# ── 1. Start vLLM ───────────────────────────────────────────── +echo "[start.sh] Starting vLLM on GPU $VLLM_GPU..." +CUDA_VISIBLE_DEVICES="$VLLM_GPU" vllm serve "$MODEL" \ + --tensor-parallel-size 1 \ + --max-model-len "$MAX_MODEL_LEN" \ + --host 127.0.0.1 \ + --port "$VLLM_PORT" \ + --api-key "$VLLM_KEY" \ + --gpu-memory-utilization "$GPU_MEM_UTIL" \ + > /tmp/vllm.log 2>&1 & + +VLLM_PID=$! +echo "[start.sh] vLLM PID=$VLLM_PID" + +# ── 2. Wait for vLLM health ─────────────────────────────────── +echo "[start.sh] Waiting for vLLM to be ready..." +WAITED=0 +MAX_WAIT=300 +while [ $WAITED -lt $MAX_WAIT ]; do + if curl -sf "http://127.0.0.1:${VLLM_PORT}/health" > /dev/null 2>&1; then + echo "[start.sh] vLLM ready after ${WAITED}s" + break + fi + if ! kill -0 $VLLM_PID 2>/dev/null; then + echo "[start.sh] ERROR: vLLM process died. Log tail:" + tail -50 /tmp/vllm.log + exit 1 + fi + sleep 1 + WAITED=$((WAITED + 1)) +done + +if [ $WAITED -ge $MAX_WAIT ]; then + echo "[start.sh] ERROR: vLLM did not become ready within ${MAX_WAIT}s. Log tail:" + tail -50 /tmp/vllm.log + exit 1 +fi + +# ── 3. Start trainer (foreground) ────────────────────────────── +echo "[start.sh] Starting trainer on GPU $TRAINER_GPU..." +CUDA_VISIBLE_DEVICES="$TRAINER_GPU" exec python3 examples/mini_swe_env/train_swe_async_grpo.py \ + --vllm-url "http://127.0.0.1:${VLLM_PORT}" \ + "$@" diff --git a/envs/mini_swe_env/pyproject.toml b/envs/mini_swe_env/pyproject.toml new file mode 100644 index 000000000..4148338db --- /dev/null +++ b/envs/mini_swe_env/pyproject.toml @@ -0,0 +1,49 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "openenv-mini-swe-env" +version = "0.1.0" +description = "Mini SWE environment for OpenEnv — SWE-Gym task training with Pi agent and Async GRPO." +requires-python = ">=3.10" +dependencies = [ + # Core OpenEnv (server + MCP). 0.3.0 ships the harness runtime. + "openenv-core[core]>=0.3.0", + "fastapi>=0.104.0", + "uvicorn[standard]>=0.24.0", + "pydantic>=2.0.0", + "fastmcp>=3.3.0", + "requests>=2.25.0", + "aiohttp>=3.13.5", + "datasets>=4.8.0", +] + +[project.optional-dependencies] +train = [ + "torch>=2.6.0", + "trl>=1.4.0", + "transformers>=5.8.0", + "accelerate>=1.13.0", + "trackio>=0.25.0", + "hf-sandbox>=0.1.1", +] +dev = [ + "pytest>=8.0.0", + "pytest-asyncio>=0.23.0", + "pytest-cov>=4.0.0", +] + +[project.scripts] +server = "mini_swe_env.server.app:main" + +[tool.setuptools] +include-package-data = true +packages = ["mini_swe_env", "mini_swe_env.server", "mini_swe_env.async_grpo"] +package-dir = { "mini_swe_env" = ".", "mini_swe_env.server" = "server", "mini_swe_env.async_grpo" = "async_grpo" } diff --git a/envs/mini_swe_env/uv.lock b/envs/mini_swe_env/uv.lock new file mode 100644 index 000000000..15ed94544 --- /dev/null +++ b/envs/mini_swe_env/uv.lock @@ -0,0 +1,4806 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11'", +] + +[[package]] +name = "accelerate" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, +] + +[[package]] +name = "aiofile" +version = "3.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "caio", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/e2/d7cb819de8df6b5c1968a2756c3cb4122d4fa2b8fc768b53b7c9e5edb646/aiofile-3.9.0.tar.gz", hash = "sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b", size = 17943, upload-time = "2024-10-08T10:39:35.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/25/da1f0b4dd970e52bf5a36c204c107e11a0c6d3ed195eba0bfbc664c312b2/aiofile-3.9.0-py3-none-any.whl", hash = "sha256:ce2f6c1571538cbdfa0143b04e16b208ecb0e9cb4148e528af8a640ed51cc8aa", size = 19539, upload-time = "2024-10-08T10:39:32.955Z" }, +] + +[[package]] +name = "aiofile" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "caio", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/41/2fea7e193e061ce54eacc3b7bc0e6a99e4fcff43c78cf0a76dd781ed8334/aiofile-3.11.1.tar.gz", hash = "sha256:1f91912c6643d2a4e49ca4ae3514f0bf3867ce948a36d99a6411b8f4755f4cf9", size = 19342, upload-time = "2026-05-16T08:18:33.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/cd/0d76dfc5de72bde52f55f53e925c7d152d9c7906634ec1e0cbc7e8d4ad93/aiofile-3.11.1-py3-none-any.whl", hash = "sha256:ce77d14ac07f77bc2b757834a5c129321f3f705c474593deed5ab209079a52c9", size = 20446, upload-time = "2026-05-16T08:18:32.051Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/85/cebc47ee74d8b408749073a1a46c6fcba13d170dc8af7e61996c6c9394ac/aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b", size = 750547, upload-time = "2026-03-31T21:56:30.024Z" }, + { url = "https://files.pythonhosted.org/packages/05/98/afd308e35b9d3d8c9ec54c0918f1d722c86dc17ddfec272fcdbcce5a3124/aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5", size = 503535, upload-time = "2026-03-31T21:56:31.935Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4d/926c183e06b09d5270a309eb50fbde7b09782bfd305dec1e800f329834fb/aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670", size = 497830, upload-time = "2026-03-31T21:56:33.654Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d6/f47d1c690f115a5c2a5e8938cce4a232a5be9aac5c5fb2647efcbbbda333/aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274", size = 1682474, upload-time = "2026-03-31T21:56:35.513Z" }, + { url = "https://files.pythonhosted.org/packages/01/44/056fd37b1bb52eac760303e5196acc74d9d546631b035704ae5927f7b4ac/aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a", size = 1655259, upload-time = "2026-03-31T21:56:37.843Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/78eb1a20c1c28ae02f6a3c0f4d7b0dcc66abce5290cadd53d78ce3084175/aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d", size = 1736204, upload-time = "2026-03-31T21:56:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/de/6c/d20d7de23f0b52b8c1d9e2033b2db1ac4dacbb470bb74c56de0f5f86bb4f/aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796", size = 1826198, upload-time = "2026-03-31T21:56:41.378Z" }, + { url = "https://files.pythonhosted.org/packages/2f/86/a6f3ff1fd795f49545a7c74b2c92f62729135d73e7e4055bf74da5a26c82/aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95", size = 1681329, upload-time = "2026-03-31T21:56:43.374Z" }, + { url = "https://files.pythonhosted.org/packages/fb/68/84cd3dab6b7b4f3e6fe9459a961acb142aaab846417f6e8905110d7027e5/aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5", size = 1560023, upload-time = "2026-03-31T21:56:45.031Z" }, + { url = "https://files.pythonhosted.org/packages/41/2c/db61b64b0249e30f954a65ab4cb4970ced57544b1de2e3c98ee5dc24165f/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a", size = 1652372, upload-time = "2026-03-31T21:56:47.075Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/e96988a6c982d047810c772e28c43c64c300c943b0ed5c1c0c4ce1e1027c/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73", size = 1662031, upload-time = "2026-03-31T21:56:48.835Z" }, + { url = "https://files.pythonhosted.org/packages/b7/26/a56feace81f3d347b4052403a9d03754a0ab23f7940780dada0849a38c92/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297", size = 1708118, upload-time = "2026-03-31T21:56:50.833Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/b6173a8ff03d01d5e1a694bc06764b5dad1df2d4ed8f0ceec12bb3277936/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074", size = 1548667, upload-time = "2026-03-31T21:56:52.81Z" }, + { url = "https://files.pythonhosted.org/packages/16/13/13296ffe2c132d888b3fe2c195c8b9c0c24c89c3fa5cc2c44464dc23b22e/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e", size = 1724490, upload-time = "2026-03-31T21:56:54.541Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1f1c287f4a79782ef36e5a6e62954c85343bc30470d862d30bd5f26c9fa2/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7", size = 1667109, upload-time = "2026-03-31T21:56:56.21Z" }, + { url = "https://files.pythonhosted.org/packages/ef/42/8461a2aaf60a8f4ea4549a4056be36b904b0eb03d97ca9a8a2604681a500/aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9", size = 439478, upload-time = "2026-03-31T21:56:58.292Z" }, + { url = "https://files.pythonhosted.org/packages/e5/71/06956304cb5ee439dfe8d86e1b2e70088bd88ed1ced1f42fb29e5d855f0e/aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76", size = 462047, upload-time = "2026-03-31T21:57:00.257Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "audioop-lts" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/53/946db57842a50b2da2e0c1e34bd37f36f5aadba1a929a3971c5d7841dbca/audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0", size = 30686, upload-time = "2025-08-05T16:43:17.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/d4/94d277ca941de5a507b07f0b592f199c22454eeaec8f008a286b3fbbacd6/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800", size = 46523, upload-time = "2025-08-05T16:42:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5a/656d1c2da4b555920ce4177167bfeb8623d98765594af59702c8873f60ec/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303", size = 27455, upload-time = "2025-08-05T16:42:22.283Z" }, + { url = "https://files.pythonhosted.org/packages/1b/83/ea581e364ce7b0d41456fb79d6ee0ad482beda61faf0cab20cbd4c63a541/audioop_lts-0.2.2-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75", size = 26997, upload-time = "2025-08-05T16:42:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/e8964210b5e216e5041593b7d33e97ee65967f17c282e8510d19c666dab4/audioop_lts-0.2.2-cp313-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d", size = 85844, upload-time = "2025-08-05T16:42:25.208Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2e/0a1c52faf10d51def20531a59ce4c706cb7952323b11709e10de324d6493/audioop_lts-0.2.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b", size = 85056, upload-time = "2025-08-05T16:42:26.559Z" }, + { url = "https://files.pythonhosted.org/packages/75/e8/cd95eef479656cb75ab05dfece8c1f8c395d17a7c651d88f8e6e291a63ab/audioop_lts-0.2.2-cp313-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8", size = 93892, upload-time = "2025-08-05T16:42:27.902Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1e/a0c42570b74f83efa5cca34905b3eef03f7ab09fe5637015df538a7f3345/audioop_lts-0.2.2-cp313-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc", size = 96660, upload-time = "2025-08-05T16:42:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/8a0ae607ca07dbb34027bac8db805498ee7bfecc05fd2c148cc1ed7646e7/audioop_lts-0.2.2-cp313-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3", size = 79143, upload-time = "2025-08-05T16:42:29.929Z" }, + { url = "https://files.pythonhosted.org/packages/12/17/0d28c46179e7910bfb0bb62760ccb33edb5de973052cb2230b662c14ca2e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6", size = 84313, upload-time = "2025-08-05T16:42:30.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/ba/bd5d3806641564f2024e97ca98ea8f8811d4e01d9b9f9831474bc9e14f9e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a", size = 93044, upload-time = "2025-08-05T16:42:31.959Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5e/435ce8d5642f1f7679540d1e73c1c42d933331c0976eb397d1717d7f01a3/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623", size = 78766, upload-time = "2025-08-05T16:42:33.302Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3b/b909e76b606cbfd53875693ec8c156e93e15a1366a012f0b7e4fb52d3c34/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7", size = 87640, upload-time = "2025-08-05T16:42:34.854Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/8f1603b4572d79b775f2140d7952f200f5e6c62904585d08a01f0a70393a/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449", size = 86052, upload-time = "2025-08-05T16:42:35.839Z" }, + { url = "https://files.pythonhosted.org/packages/b5/96/c37846df657ccdda62ba1ae2b6534fa90e2e1b1742ca8dcf8ebd38c53801/audioop_lts-0.2.2-cp313-abi3-win32.whl", hash = "sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636", size = 26185, upload-time = "2025-08-05T16:42:37.04Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/9d78fdb5b844a83da8a71226c7bdae7cc638861085fff7a1d707cb4823fa/audioop_lts-0.2.2-cp313-abi3-win_amd64.whl", hash = "sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e", size = 30503, upload-time = "2025-08-05T16:42:38.427Z" }, + { url = "https://files.pythonhosted.org/packages/34/25/20d8fde083123e90c61b51afb547bb0ea7e77bab50d98c0ab243d02a0e43/audioop_lts-0.2.2-cp313-abi3-win_arm64.whl", hash = "sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f", size = 24173, upload-time = "2025-08-05T16:42:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/58/a7/0a764f77b5c4ac58dc13c01a580f5d32ae8c74c92020b961556a43e26d02/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09", size = 47096, upload-time = "2025-08-05T16:42:40.684Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ed/ebebedde1a18848b085ad0fa54b66ceb95f1f94a3fc04f1cd1b5ccb0ed42/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58", size = 27748, upload-time = "2025-08-05T16:42:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/cb/6e/11ca8c21af79f15dbb1c7f8017952ee8c810c438ce4e2b25638dfef2b02c/audioop_lts-0.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19", size = 27329, upload-time = "2025-08-05T16:42:42.987Z" }, + { url = "https://files.pythonhosted.org/packages/84/52/0022f93d56d85eec5da6b9da6a958a1ef09e80c39f2cc0a590c6af81dcbb/audioop_lts-0.2.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911", size = 92407, upload-time = "2025-08-05T16:42:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/87/1d/48a889855e67be8718adbc7a01f3c01d5743c325453a5e81cf3717664aad/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9", size = 91811, upload-time = "2025-08-05T16:42:45.325Z" }, + { url = "https://files.pythonhosted.org/packages/98/a6/94b7213190e8077547ffae75e13ed05edc488653c85aa5c41472c297d295/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe", size = 100470, upload-time = "2025-08-05T16:42:46.468Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/78450d7cb921ede0cfc33426d3a8023a3bda755883c95c868ee36db8d48d/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132", size = 103878, upload-time = "2025-08-05T16:42:47.576Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e2/cd5439aad4f3e34ae1ee852025dc6aa8f67a82b97641e390bf7bd9891d3e/audioop_lts-0.2.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753", size = 84867, upload-time = "2025-08-05T16:42:49.003Z" }, + { url = "https://files.pythonhosted.org/packages/68/4b/9d853e9076c43ebba0d411e8d2aa19061083349ac695a7d082540bad64d0/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb", size = 90001, upload-time = "2025-08-05T16:42:50.038Z" }, + { url = "https://files.pythonhosted.org/packages/58/26/4bae7f9d2f116ed5593989d0e521d679b0d583973d203384679323d8fa85/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093", size = 99046, upload-time = "2025-08-05T16:42:51.111Z" }, + { url = "https://files.pythonhosted.org/packages/b2/67/a9f4fb3e250dda9e9046f8866e9fa7d52664f8985e445c6b4ad6dfb55641/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7", size = 84788, upload-time = "2025-08-05T16:42:52.198Z" }, + { url = "https://files.pythonhosted.org/packages/70/f7/3de86562db0121956148bcb0fe5b506615e3bcf6e63c4357a612b910765a/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c", size = 94472, upload-time = "2025-08-05T16:42:53.59Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/fd772bf9078ae1001207d2df1eef3da05bea611a87dd0e8217989b2848fa/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5", size = 92279, upload-time = "2025-08-05T16:42:54.632Z" }, + { url = "https://files.pythonhosted.org/packages/4f/41/affea7181592ab0ab560044632571a38edaf9130b84928177823fbf3176a/audioop_lts-0.2.2-cp313-cp313t-win32.whl", hash = "sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917", size = 26568, upload-time = "2025-08-05T16:42:55.627Z" }, + { url = "https://files.pythonhosted.org/packages/28/2b/0372842877016641db8fc54d5c88596b542eec2f8f6c20a36fb6612bf9ee/audioop_lts-0.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547", size = 30942, upload-time = "2025-08-05T16:42:56.674Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/baf2b9cc7e96c179bb4a54f30fcd83e6ecb340031bde68f486403f943768/audioop_lts-0.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969", size = 24603, upload-time = "2025-08-05T16:42:57.571Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/413b5a2804091e2c7d5def1d618e4837f1cb82464e230f827226278556b7/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6", size = 47104, upload-time = "2025-08-05T16:42:58.518Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/daa3308dc6593944410c2c68306a5e217f5c05b70a12e70228e7dd42dc5c/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a", size = 27754, upload-time = "2025-08-05T16:43:00.132Z" }, + { url = "https://files.pythonhosted.org/packages/4e/86/c2e0f627168fcf61781a8f72cab06b228fe1da4b9fa4ab39cfb791b5836b/audioop_lts-0.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b", size = 27332, upload-time = "2025-08-05T16:43:01.666Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/35dce665255434f54e5307de39e31912a6f902d4572da7c37582809de14f/audioop_lts-0.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6", size = 92396, upload-time = "2025-08-05T16:43:02.991Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d2/deeb9f51def1437b3afa35aeb729d577c04bcd89394cb56f9239a9f50b6f/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf", size = 91811, upload-time = "2025-08-05T16:43:04.096Z" }, + { url = "https://files.pythonhosted.org/packages/76/3b/09f8b35b227cee28cc8231e296a82759ed80c1a08e349811d69773c48426/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd", size = 100483, upload-time = "2025-08-05T16:43:05.085Z" }, + { url = "https://files.pythonhosted.org/packages/0b/15/05b48a935cf3b130c248bfdbdea71ce6437f5394ee8533e0edd7cfd93d5e/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a", size = 103885, upload-time = "2025-08-05T16:43:06.197Z" }, + { url = "https://files.pythonhosted.org/packages/83/80/186b7fce6d35b68d3d739f228dc31d60b3412105854edb975aa155a58339/audioop_lts-0.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e", size = 84899, upload-time = "2025-08-05T16:43:07.291Z" }, + { url = "https://files.pythonhosted.org/packages/49/89/c78cc5ac6cb5828f17514fb12966e299c850bc885e80f8ad94e38d450886/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7", size = 89998, upload-time = "2025-08-05T16:43:08.335Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/6401888d0c010e586c2ca50fce4c903d70a6bb55928b16cfbdfd957a13da/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5", size = 99046, upload-time = "2025-08-05T16:43:09.367Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/c874ca9bb447dae0e2ef2e231f6c4c2b0c39e31ae684d2420b0f9e97ee68/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9", size = 84843, upload-time = "2025-08-05T16:43:10.749Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/0323e66f3daebc13fd46b36b30c3be47e3fc4257eae44f1e77eb828c703f/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602", size = 94490, upload-time = "2025-08-05T16:43:12.131Z" }, + { url = "https://files.pythonhosted.org/packages/98/6b/acc7734ac02d95ab791c10c3f17ffa3584ccb9ac5c18fd771c638ed6d1f5/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0", size = 92297, upload-time = "2025-08-05T16:43:13.139Z" }, + { url = "https://files.pythonhosted.org/packages/13/c3/c3dc3f564ce6877ecd2a05f8d751b9b27a8c320c2533a98b0c86349778d0/audioop_lts-0.2.2-cp314-cp314t-win32.whl", hash = "sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3", size = 27331, upload-time = "2025-08-05T16:43:14.19Z" }, + { url = "https://files.pythonhosted.org/packages/72/bb/b4608537e9ffcb86449091939d52d24a055216a36a8bf66b936af8c3e7ac/audioop_lts-0.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b", size = 31697, upload-time = "2025-08-05T16:43:15.193Z" }, + { url = "https://files.pythonhosted.org/packages/f6/22/91616fe707a5c5510de2cac9b046a30defe7007ba8a0c04f9c08f27df312/audioop_lts-0.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd", size = 25206, upload-time = "2025-08-05T16:43:16.444Z" }, +] + +[[package]] +name = "authlib" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "joserfc" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511, upload-time = "2026-05-06T08:10:23.116Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" }, +] + +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, +] + +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + +[[package]] +name = "brotli" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/10/a090475284fc4a71aed40a96f32e44a7fe5bda39687353dd977720b211b6/brotli-1.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b90b767916ac44e93a8e28ce6adf8d551e43affb512f2377c732d486ac6514e", size = 863089, upload-time = "2025-11-05T18:38:01.181Z" }, + { url = "https://files.pythonhosted.org/packages/03/41/17416630e46c07ac21e378c3464815dd2e120b441e641bc516ac32cc51d2/brotli-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6be67c19e0b0c56365c6a76e393b932fb0e78b3b56b711d180dd7013cb1fd984", size = 445442, upload-time = "2025-11-05T18:38:02.434Z" }, + { url = "https://files.pythonhosted.org/packages/24/31/90cc06584deb5d4fcafc0985e37741fc6b9717926a78674bbb3ce018957e/brotli-1.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0bbd5b5ccd157ae7913750476d48099aaf507a79841c0d04a9db4415b14842de", size = 1532658, upload-time = "2025-11-05T18:38:03.588Z" }, + { url = "https://files.pythonhosted.org/packages/62/17/33bf0c83bcbc96756dfd712201d87342732fad70bb3472c27e833a44a4f9/brotli-1.2.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3f3c908bcc404c90c77d5a073e55271a0a498f4e0756e48127c35d91cf155947", size = 1631241, upload-time = "2025-11-05T18:38:04.582Z" }, + { url = "https://files.pythonhosted.org/packages/48/10/f47854a1917b62efe29bc98ac18e5d4f71df03f629184575b862ef2e743b/brotli-1.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b557b29782a643420e08d75aea889462a4a8796e9a6cf5621ab05a3f7da8ef2", size = 1424307, upload-time = "2025-11-05T18:38:05.587Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b7/f88eb461719259c17483484ea8456925ee057897f8e64487d76e24e5e38d/brotli-1.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81da1b229b1889f25adadc929aeb9dbc4e922bd18561b65b08dd9343cfccca84", size = 1488208, upload-time = "2025-11-05T18:38:06.613Z" }, + { url = "https://files.pythonhosted.org/packages/26/59/41bbcb983a0c48b0b8004203e74706c6b6e99a04f3c7ca6f4f41f364db50/brotli-1.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ff09cd8c5eec3b9d02d2408db41be150d8891c5566addce57513bf546e3d6c6d", size = 1597574, upload-time = "2025-11-05T18:38:07.838Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e6/8c89c3bdabbe802febb4c5c6ca224a395e97913b5df0dff11b54f23c1788/brotli-1.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a1778532b978d2536e79c05dac2d8cd857f6c55cd0c95ace5b03740824e0e2f1", size = 1492109, upload-time = "2025-11-05T18:38:08.816Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9a/4b19d4310b2dbd545c0c33f176b0528fa68c3cd0754e34b2f2bcf56548ae/brotli-1.2.0-cp310-cp310-win32.whl", hash = "sha256:b232029d100d393ae3c603c8ffd7e3fe6f798c5e28ddca5feabb8e8fdb732997", size = 334461, upload-time = "2025-11-05T18:38:10.729Z" }, + { url = "https://files.pythonhosted.org/packages/ac/39/70981d9f47705e3c2b95c0847dfa3e7a37aa3b7c6030aedc4873081ed005/brotli-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef87b8ab2704da227e83a246356a2b179ef826f550f794b2c52cddb4efbd0196", size = 369035, upload-time = "2025-11-05T18:38:11.827Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ef/f285668811a9e1ddb47a18cb0b437d5fc2760d537a2fe8a57875ad6f8448/brotli-1.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744", size = 863110, upload-time = "2025-11-05T18:38:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/50/62/a3b77593587010c789a9d6eaa527c79e0848b7b860402cc64bc0bc28a86c/brotli-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f", size = 445438, upload-time = "2025-11-05T18:38:14.208Z" }, + { url = "https://files.pythonhosted.org/packages/cd/e1/7fadd47f40ce5549dc44493877db40292277db373da5053aff181656e16e/brotli-1.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd", size = 1534420, upload-time = "2025-11-05T18:38:15.111Z" }, + { url = "https://files.pythonhosted.org/packages/12/8b/1ed2f64054a5a008a4ccd2f271dbba7a5fb1a3067a99f5ceadedd4c1d5a7/brotli-1.2.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe", size = 1632619, upload-time = "2025-11-05T18:38:16.094Z" }, + { url = "https://files.pythonhosted.org/packages/89/5a/7071a621eb2d052d64efd5da2ef55ecdac7c3b0c6e4f9d519e9c66d987ef/brotli-1.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a", size = 1426014, upload-time = "2025-11-05T18:38:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/26/6d/0971a8ea435af5156acaaccec1a505f981c9c80227633851f2810abd252a/brotli-1.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b", size = 1489661, upload-time = "2025-11-05T18:38:18.41Z" }, + { url = "https://files.pythonhosted.org/packages/f3/75/c1baca8b4ec6c96a03ef8230fab2a785e35297632f402ebb1e78a1e39116/brotli-1.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3", size = 1599150, upload-time = "2025-11-05T18:38:19.792Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1a/23fcfee1c324fd48a63d7ebf4bac3a4115bdb1b00e600f80f727d850b1ae/brotli-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae", size = 1493505, upload-time = "2025-11-05T18:38:20.913Z" }, + { url = "https://files.pythonhosted.org/packages/36/e5/12904bbd36afeef53d45a84881a4810ae8810ad7e328a971ebbfd760a0b3/brotli-1.2.0-cp311-cp311-win32.whl", hash = "sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03", size = 334451, upload-time = "2025-11-05T18:38:21.94Z" }, + { url = "https://files.pythonhosted.org/packages/02/8b/ecb5761b989629a4758c394b9301607a5880de61ee2ee5fe104b87149ebc/brotli-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24", size = 369035, upload-time = "2025-11-05T18:38:22.941Z" }, + { url = "https://files.pythonhosted.org/packages/11/ee/b0a11ab2315c69bb9b45a2aaed022499c9c24a205c3a49c3513b541a7967/brotli-1.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84", size = 861543, upload-time = "2025-11-05T18:38:24.183Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2f/29c1459513cd35828e25531ebfcbf3e92a5e49f560b1777a9af7203eb46e/brotli-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b", size = 444288, upload-time = "2025-11-05T18:38:25.139Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/feba03130d5fceadfa3a1bb102cb14650798c848b1df2a808356f939bb16/brotli-1.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d", size = 1528071, upload-time = "2025-11-05T18:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/2b/38/f3abb554eee089bd15471057ba85f47e53a44a462cfce265d9bf7088eb09/brotli-1.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca", size = 1626913, upload-time = "2025-11-05T18:38:27.284Z" }, + { url = "https://files.pythonhosted.org/packages/03/a7/03aa61fbc3c5cbf99b44d158665f9b0dd3d8059be16c460208d9e385c837/brotli-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f", size = 1419762, upload-time = "2025-11-05T18:38:28.295Z" }, + { url = "https://files.pythonhosted.org/packages/21/1b/0374a89ee27d152a5069c356c96b93afd1b94eae83f1e004b57eb6ce2f10/brotli-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28", size = 1484494, upload-time = "2025-11-05T18:38:29.29Z" }, + { url = "https://files.pythonhosted.org/packages/cf/57/69d4fe84a67aef4f524dcd075c6eee868d7850e85bf01d778a857d8dbe0a/brotli-1.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7", size = 1593302, upload-time = "2025-11-05T18:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3b/39e13ce78a8e9a621c5df3aeb5fd181fcc8caba8c48a194cd629771f6828/brotli-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036", size = 1487913, upload-time = "2025-11-05T18:38:31.618Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/4d00cb9bd76a6357a66fcd54b4b6d70288385584063f4b07884c1e7286ac/brotli-1.2.0-cp312-cp312-win32.whl", hash = "sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161", size = 334362, upload-time = "2025-11-05T18:38:32.939Z" }, + { url = "https://files.pythonhosted.org/packages/1c/4e/bc1dcac9498859d5e353c9b153627a3752868a9d5f05ce8dedd81a2354ab/brotli-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44", size = 369115, upload-time = "2025-11-05T18:38:33.765Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d4/4ad5432ac98c73096159d9ce7ffeb82d151c2ac84adcc6168e476bb54674/brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab", size = 861523, upload-time = "2025-11-05T18:38:34.67Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/9cc5bd03ee68a85dc4bc89114f7067c056a3c14b3d95f171918c088bf88d/brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c", size = 444289, upload-time = "2025-11-05T18:38:35.6Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b6/fe84227c56a865d16a6614e2c4722864b380cb14b13f3e6bef441e73a85a/brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f", size = 1528076, upload-time = "2025-11-05T18:38:36.639Z" }, + { url = "https://files.pythonhosted.org/packages/55/de/de4ae0aaca06c790371cf6e7ee93a024f6b4bb0568727da8c3de112e726c/brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6", size = 1626880, upload-time = "2025-11-05T18:38:37.623Z" }, + { url = "https://files.pythonhosted.org/packages/5f/16/a1b22cbea436642e071adcaf8d4b350a2ad02f5e0ad0da879a1be16188a0/brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c", size = 1419737, upload-time = "2025-11-05T18:38:38.729Z" }, + { url = "https://files.pythonhosted.org/packages/46/63/c968a97cbb3bdbf7f974ef5a6ab467a2879b82afbc5ffb65b8acbb744f95/brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48", size = 1484440, upload-time = "2025-11-05T18:38:39.916Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/102c67ea5c9fc171f423e8399e585dabea29b5bc79b05572891e70013cdd/brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18", size = 1593313, upload-time = "2025-11-05T18:38:41.24Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4a/9526d14fa6b87bc827ba1755a8440e214ff90de03095cacd78a64abe2b7d/brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5", size = 1487945, upload-time = "2025-11-05T18:38:42.277Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e8/3fe1ffed70cbef83c5236166acaed7bb9c766509b157854c80e2f766b38c/brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a", size = 334368, upload-time = "2025-11-05T18:38:43.345Z" }, + { url = "https://files.pythonhosted.org/packages/ff/91/e739587be970a113b37b821eae8097aac5a48e5f0eca438c22e4c7dd8648/brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8", size = 369116, upload-time = "2025-11-05T18:38:44.609Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/298c2ddf786bb7347a1cd71d63a347a79e5712a7c0cba9e3c3458ebd976f/brotli-1.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21", size = 863080, upload-time = "2025-11-05T18:38:45.503Z" }, + { url = "https://files.pythonhosted.org/packages/84/0c/aac98e286ba66868b2b3b50338ffbd85a35c7122e9531a73a37a29763d38/brotli-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac", size = 445453, upload-time = "2025-11-05T18:38:46.433Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f1/0ca1f3f99ae300372635ab3fe2f7a79fa335fee3d874fa7f9e68575e0e62/brotli-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e", size = 1528168, upload-time = "2025-11-05T18:38:47.371Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a6/2ebfc8f766d46df8d3e65b880a2e220732395e6d7dc312c1e1244b0f074a/brotli-1.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7", size = 1627098, upload-time = "2025-11-05T18:38:48.385Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/0976d5b097ff8a22163b10617f76b2557f15f0f39d6a0fe1f02b1a53e92b/brotli-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63", size = 1419861, upload-time = "2025-11-05T18:38:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/d76df7176a2ce7616ff94c1fb72d307c9a30d2189fe877f3dd99af00ea5a/brotli-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b", size = 1484594, upload-time = "2025-11-05T18:38:50.655Z" }, + { url = "https://files.pythonhosted.org/packages/d3/93/14cf0b1216f43df5609f5b272050b0abd219e0b54ea80b47cef9867b45e7/brotli-1.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361", size = 1593455, upload-time = "2025-11-05T18:38:51.624Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/3183c9e41ca755713bdf2cc1d0810df742c09484e2e1ddd693bee53877c1/brotli-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888", size = 1488164, upload-time = "2025-11-05T18:38:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/64/6a/0c78d8f3a582859236482fd9fa86a65a60328a00983006bcf6d83b7b2253/brotli-1.2.0-cp314-cp314-win32.whl", hash = "sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d", size = 339280, upload-time = "2025-11-05T18:38:54.02Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" }, +] + +[[package]] +name = "cachetools" +version = "7.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/53/984d70974279207f676fbd525cbe7533b95da34d829f2adc0797a6860718/cachetools-7.1.2.tar.gz", hash = "sha256:c1373e3cad0933dfb46bb04d04ef67b5204f8220eb906096dd89a76196053d57", size = 39828, upload-time = "2026-05-16T19:59:03.565Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/9b/56cf24737a6756d8751659c8809a67c23b7b256a587bcb147a6d24fddea3/cachetools-7.1.2-py3-none-any.whl", hash = "sha256:89386be5bece29963e0f22bb7e1aba91c8395c7ad107780e2ce7af3ab315ae40", size = 16805, upload-time = "2026-05-16T19:59:01.927Z" }, +] + +[[package]] +name = "caio" +version = "0.9.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/88/b8527e1b00c1811db339a1df8bd1ae49d146fcea9d6a5c40e3a80aaeb38d/caio-0.9.25.tar.gz", hash = "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10", size = 26781, upload-time = "2025-12-26T15:21:36.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/80/ea4ead0c5d52a9828692e7df20f0eafe8d26e671ce4883a0a146bb91049e/caio-0.9.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca6c8ecda611478b6016cb94d23fd3eb7124852b985bdec7ecaad9f3116b9619", size = 36836, upload-time = "2025-12-26T15:22:04.662Z" }, + { url = "https://files.pythonhosted.org/packages/17/b9/36715c97c873649d1029001578f901b50250916295e3dddf20c865438865/caio-0.9.25-cp310-cp310-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db9b5681e4af8176159f0d6598e73b2279bb661e718c7ac23342c550bd78c241", size = 79695, upload-time = "2025-12-26T15:22:18.818Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ab/07080ecb1adb55a02cbd8ec0126aa8e43af343ffabb6a71125b42670e9a1/caio-0.9.25-cp310-cp310-manylinux_2_34_aarch64.whl", hash = "sha256:bf61d7d0c4fd10ffdd98ca47f7e8db4d7408e74649ffaf4bef40b029ada3c21b", size = 79457, upload-time = "2026-03-04T22:08:16.024Z" }, + { url = "https://files.pythonhosted.org/packages/88/95/dd55757bb671eb4c376e006c04e83beb413486821f517792ea603ef216e9/caio-0.9.25-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:ab52e5b643f8bbd64a0605d9412796cd3464cb8ca88593b13e95a0f0b10508ae", size = 77705, upload-time = "2026-03-04T22:08:17.202Z" }, + { url = "https://files.pythonhosted.org/packages/ec/90/543f556fcfcfa270713eef906b6352ab048e1e557afec12925c991dc93c2/caio-0.9.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d6956d9e4a27021c8bd6c9677f3a59eb1d820cc32d0343cea7961a03b1371965", size = 36839, upload-time = "2025-12-26T15:21:40.267Z" }, + { url = "https://files.pythonhosted.org/packages/51/3b/36f3e8ec38dafe8de4831decd2e44c69303d2a3892d16ceda42afed44e1b/caio-0.9.25-cp311-cp311-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bf84bfa039f25ad91f4f52944452a5f6f405e8afab4d445450978cd6241d1478", size = 80255, upload-time = "2025-12-26T15:22:20.271Z" }, + { url = "https://files.pythonhosted.org/packages/df/ce/65e64867d928e6aff1b4f0e12dba0ef6d5bf412c240dc1df9d421ac10573/caio-0.9.25-cp311-cp311-manylinux_2_34_aarch64.whl", hash = "sha256:ae3d62587332bce600f861a8de6256b1014d6485cfd25d68c15caf1611dd1f7c", size = 80052, upload-time = "2026-03-04T22:08:20.402Z" }, + { url = "https://files.pythonhosted.org/packages/46/90/e278863c47e14ec58309aa2e38a45882fbe67b4cc29ec9bc8f65852d3e45/caio-0.9.25-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:fc220b8533dcf0f238a6b1a4a937f92024c71e7b10b5a2dfc1c73604a25709bc", size = 78273, upload-time = "2026-03-04T22:08:21.368Z" }, + { url = "https://files.pythonhosted.org/packages/d3/25/79c98ebe12df31548ba4eaf44db11b7cad6b3e7b4203718335620939083c/caio-0.9.25-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb7ff95af4c31ad3f03179149aab61097a71fd85e05f89b4786de0359dffd044", size = 36983, upload-time = "2025-12-26T15:21:36.075Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2b/21288691f16d479945968a0a4f2856818c1c5be56881d51d4dac9b255d26/caio-0.9.25-cp312-cp312-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:97084e4e30dfa598449d874c4d8e0c8d5ea17d2f752ef5e48e150ff9d240cd64", size = 82012, upload-time = "2025-12-26T15:22:20.983Z" }, + { url = "https://files.pythonhosted.org/packages/03/c4/8a1b580875303500a9c12b9e0af58cb82e47f5bcf888c2457742a138273c/caio-0.9.25-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:4fa69eba47e0f041b9d4f336e2ad40740681c43e686b18b191b6c5f4c5544bfb", size = 81502, upload-time = "2026-03-04T22:08:22.381Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/0fe770b8ffc8362c48134d1592d653a81a3d8748d764bec33864db36319d/caio-0.9.25-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:6bebf6f079f1341d19f7386db9b8b1f07e8cc15ae13bfdaff573371ba0575d69", size = 80200, upload-time = "2026-03-04T22:08:23.382Z" }, + { url = "https://files.pythonhosted.org/packages/31/57/5e6ff127e6f62c9f15d989560435c642144aa4210882f9494204bc892305/caio-0.9.25-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451", size = 36979, upload-time = "2025-12-26T15:21:35.484Z" }, + { url = "https://files.pythonhosted.org/packages/a3/9f/f21af50e72117eb528c422d4276cbac11fb941b1b812b182e0a9c70d19c5/caio-0.9.25-cp313-cp313-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6", size = 81900, upload-time = "2025-12-26T15:22:21.919Z" }, + { url = "https://files.pythonhosted.org/packages/9c/12/c39ae2a4037cb10ad5eb3578eb4d5f8c1a2575c62bba675f3406b7ef0824/caio-0.9.25-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:1a177d4777141b96f175fe2c37a3d96dec7911ed9ad5f02bac38aaa1c936611f", size = 81523, upload-time = "2026-03-04T22:08:25.187Z" }, + { url = "https://files.pythonhosted.org/packages/22/59/f8f2e950eb4f1a5a3883e198dca514b9d475415cb6cd7b78b9213a0dd45a/caio-0.9.25-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:9ed3cfb28c0e99fec5e208c934e5c157d0866aa9c32aa4dc5e9b6034af6286b7", size = 80243, upload-time = "2026-03-04T22:08:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/69/ca/a08fdc7efdcc24e6a6131a93c85be1f204d41c58f474c42b0670af8c016b/caio-0.9.25-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fab6078b9348e883c80a5e14b382e6ad6aabbc4429ca034e76e730cf464269db", size = 36978, upload-time = "2025-12-26T15:21:41.055Z" }, + { url = "https://files.pythonhosted.org/packages/5e/6c/d4d24f65e690213c097174d26eda6831f45f4734d9d036d81790a27e7b78/caio-0.9.25-cp314-cp314-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:44a6b58e52d488c75cfaa5ecaa404b2b41cc965e6c417e03251e868ecd5b6d77", size = 81832, upload-time = "2025-12-26T15:22:22.757Z" }, + { url = "https://files.pythonhosted.org/packages/87/a4/e534cf7d2d0e8d880e25dd61e8d921ffcfe15bd696734589826f5a2df727/caio-0.9.25-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:628a630eb7fb22381dd8e3c8ab7f59e854b9c806639811fc3f4310c6bd711d79", size = 81565, upload-time = "2026-03-04T22:08:27.483Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ed/bf81aeac1d290017e5e5ac3e880fd56ee15e50a6d0353986799d1bc5cfd5/caio-0.9.25-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:0ba16aa605ccb174665357fc729cf500679c2d94d5f1458a6f0d5ca48f2060a7", size = 80071, upload-time = "2026-03-04T22:08:28.751Z" }, + { url = "https://files.pythonhosted.org/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087, upload-time = "2025-12-26T15:22:00.221Z" }, +] + +[[package]] +name = "certifi" +version = "2026.4.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, + { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, + { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, + { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, + { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e4/796662cd90cf80e3a363c99db2b88e0e394b988a575f60a17e16440cd011/click-8.4.0.tar.gz", hash = "sha256:638f1338fe1235c8f4e008e4a8a254fb5c5fbdcbb40ece3c9142ebb78e792973", size = 350843, upload-time = "2026-05-17T00:47:58.425Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/ae/8e92f8058baf87f6c7d86ee7e457668690195cc77efedb8d3797a06e3940/click-8.4.0-py3-none-any.whl", hash = "sha256:40c50b7c6c6adac2823d411041ec84f3f103f1b280d5e9ce0d7f998995832f81", size = 116147, upload-time = "2026-05-17T00:47:56.842Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/7f/d0720730a397a999ffc0fd3f5bebef347338e3a47b727da66fbb228e2ff2/coverage-7.14.0.tar.gz", hash = "sha256:057a6af2f160a85384cde4ab36f0d2777bae1057bae255f95413cdd382aa5c74", size = 919489, upload-time = "2026-05-10T18:02:31.397Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/9d/7c83ef51c3eb495f10010094e661833588b7709946da634c8b66520b97c7/coverage-7.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84c32d90bf4537f0e7b4dec9aaa9a938fb8205136b9d2ecf4d7629d5262dc075", size = 219668, upload-time = "2026-05-10T17:59:23.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/34/898546aefbd28f0af131201d0dc852c9e976f817bd7d5bfb8dc4e02863bb/coverage-7.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7c843572c605ab51cfdb5c6b5f2586e2a8467c0d28eca4bdef4ec70c5fecbd82", size = 220192, upload-time = "2026-05-10T17:59:26.095Z" }, + { url = "https://files.pythonhosted.org/packages/df/4a/b457c88aca72b0df13a98167ebd5d947135ccd9881ea88ce6a570e13aa9b/coverage-7.14.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0c451757d3fa2603354fdc789b5e58a0e327a117c370a40e3476ba4eabab228c", size = 246932, upload-time = "2026-05-10T17:59:27.806Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d9/92600e89486fd074c50f0117422b2c9592c3e144e2f25bd5ac0bc62bc7a0/coverage-7.14.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3fd43f0616e765ab78d069cf8358def7363957a45cee446d65c502dcfeea7893", size = 248762, upload-time = "2026-05-10T17:59:29.479Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e1/9ea1eb9c311da7f15853559dc1d9d82bef88ecd3e59fbeb51f16bc2ffa91/coverage-7.14.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:731e535b1498b27d13594a0527a79b0510867b0ad891532be41cb883f2128e20", size = 250625, upload-time = "2026-05-10T17:59:31.33Z" }, + { url = "https://files.pythonhosted.org/packages/a5/03/57afca1b8106f8549a5329139315041fe166d6099bd9381346b9430dfbd1/coverage-7.14.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c7492f2d493b976941c7ca050f273cbda2f43c381124f7586a3e3c16d1804fec", size = 252539, upload-time = "2026-05-10T17:59:32.692Z" }, + { url = "https://files.pythonhosted.org/packages/57/5e/2e9fc63c9928119c1dbae02222be51407d3e7ebac5811ebbda4af3557795/coverage-7.14.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc38367eaa2abb1b766ac333142bce7655335a73537f5c8b75aaa89c2b987757", size = 247636, upload-time = "2026-05-10T17:59:34.599Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e2/0b7898cda21041cc67546e19b80ba66cbbb47cbece52a76a5904de6a3aaf/coverage-7.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0a951308cde22cf77f953955a754d04dccb57fe3bb8e345d685778ed9fc1632a", size = 248666, upload-time = "2026-05-10T17:59:36.232Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/d33662a2fdaef23229c15921f39c84ec38441f3069ba26e134ed402c833b/coverage-7.14.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fab3877e4ebb06bd9d4d4d00ee53309ee5478e66873c66a382272e3ee33eb7ea", size = 246670, upload-time = "2026-05-10T17:59:38.029Z" }, + { url = "https://files.pythonhosted.org/packages/99/b2/533942c3bfbf6770b5c32d7f2ff029fe013dba31f3fe8b45cabbb250365e/coverage-7.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b812eb847b19876ebf33fb6c4f11819af05ab6050b0bfa1bc53412ae81779adb", size = 250484, upload-time = "2026-05-10T17:59:39.974Z" }, + { url = "https://files.pythonhosted.org/packages/d8/00/15acbad83a96de13c73831486c7627bfed73dfaec53b04e4a6315edf3fd8/coverage-7.14.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d9c8ef6ed820c433de075657d72dda1f89a2984955e58b8a75feb3f184250218", size = 246942, upload-time = "2026-05-10T17:59:41.659Z" }, + { url = "https://files.pythonhosted.org/packages/70/db/cef0228de493f2c740c760a9057a61d00c6849480073b70a75b87c7d4bab/coverage-7.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d128b1bba9361fbaaf6a19e179e6cfd6a9103ce0c0555876f72780acc93efd85", size = 247544, upload-time = "2026-05-10T17:59:43.471Z" }, + { url = "https://files.pythonhosted.org/packages/77/a0/d9ef8e148f3025c2ae8401d77cda1502b6d2a4d8102603a8af31460aedb6/coverage-7.14.0-cp310-cp310-win32.whl", hash = "sha256:65f267ca1370726ec2c1aa38bbe4df9a71a740f22878d2d4bf59d71a4cd8d323", size = 222285, upload-time = "2026-05-10T17:59:44.908Z" }, + { url = "https://files.pythonhosted.org/packages/85/c0/30c454c7d3cf47b2805d4e06f12443f5eece8a5d030d3b0350e7b74ecb49/coverage-7.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:b34ece8065914f938ed7f2c5872bb865336977a52919149846eac3744327267a", size = 223215, upload-time = "2026-05-10T17:59:46.779Z" }, + { url = "https://files.pythonhosted.org/packages/fc/e4/649c8d4f7f1709b6dbfc474358aa1bba02f67bcd52e2fec291a5014006cd/coverage-7.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a78e2a9d9c5e3b8d4ab9b9d28c985ea66fced0a7d7c2aec1f216e03a2011480", size = 219795, upload-time = "2026-05-10T17:59:48.198Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8d/46692d24b3f395d4cbf17bfcc57136b4f2f9c0c0df864b0bddfc1d71a014/coverage-7.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1816c505187592dcd1c5a5f226601a549f70365fbd00930ac88b0c225b76bb4", size = 220299, upload-time = "2026-05-10T17:59:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/12/c2/a40f5cb295bbcbb697a76947a56081c494c61950366294ee426ffe261099/coverage-7.14.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d8e1762f0e9cbc26ec315471e7b47855218e833cd5a032d706fbf43845d878c7", size = 250721, upload-time = "2026-05-10T17:59:51.494Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/202235eb5c3c14c212462cd91d61b7386bf8fc44bc7a77f4742d2a69174b/coverage-7.14.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9336e23e8bb3a3925398261385e2a1533957d3e760e91070dcb0e98bfa514eed", size = 252633, upload-time = "2026-05-10T17:59:53.244Z" }, + { url = "https://files.pythonhosted.org/packages/bb/80/5f596e8995785124ee191c42535664c5e62c65995b66f4ca21e28ae04c81/coverage-7.14.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd1169b2230f9cbe9c638ba38022ed7a2b1e641cc07f7cea0365e4be2a74980", size = 254743, upload-time = "2026-05-10T17:59:55.021Z" }, + { url = "https://files.pythonhosted.org/packages/1e/6d/0d178825be2350f0adb27984d0aa7cf84bbdab201f6fb926b535d23a8f5f/coverage-7.14.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d1bb3543b58fea74d2cd1abc4054cc927e4724687cb4560cd2ed88d2c7d820c0", size = 256700, upload-time = "2026-05-10T17:59:56.511Z" }, + { url = "https://files.pythonhosted.org/packages/19/5b/9e549c2f6e9dfea472adadba06c294e64735dabc2dd19015fac082095013/coverage-7.14.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a93bac2cb577ef60074999ed56d8a1535894398e2ed920d4185c3ec0c8864742", size = 250854, upload-time = "2026-05-10T17:59:57.94Z" }, + { url = "https://files.pythonhosted.org/packages/3d/1c/b94f9f5f36396021ee2f62c5834b12e6a3d31f0bed5d6fc6d1c3caec087c/coverage-7.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5904abf7e18cddc463219b17552229650c6b79e061d31a1059283051169cf7d5", size = 252433, upload-time = "2026-05-10T17:59:59.688Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cb/d192cd8e1345eccabc32016f2d39072ecd10cb4f4b983ed8d0ebdeaf00dc/coverage-7.14.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:741f57cddc9004a8c81b084660215f33a6b597dbe62c31386b983ee26310e327", size = 250494, upload-time = "2026-05-10T18:00:01.953Z" }, + { url = "https://files.pythonhosted.org/packages/53/c5/aac9f460a41d835dbddef1d377f105f6ac2311d0f3c1588e9f51046d8813/coverage-7.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:664123feb0929d7affc135717dbd70d61d98688a08ab1e5ba464739620c6252d", size = 254261, upload-time = "2026-05-10T18:00:03.779Z" }, + { url = "https://files.pythonhosted.org/packages/23/aa/7af7c0081980a9cb3d289c5a435a4b7657dcecbd128e25c580e6a50389b5/coverage-7.14.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c83d2399a51bbec8429266905d33616f04bc5726b1138c35844d5fcd896b2e20", size = 250216, upload-time = "2026-05-10T18:00:05.262Z" }, + { url = "https://files.pythonhosted.org/packages/35/60/a4257538ce2f6b978aeb51870d6c4208c510928a03db7e0339bb625dccb7/coverage-7.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb2e855b87321259a037429288ae85216d191c74de3e79bf57cd2bc0761992c", size = 251125, upload-time = "2026-05-10T18:00:06.858Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ab/f91af47642ec1aa53490e835a95847168d9c77fc39aa58527604c051e145/coverage-7.14.0-cp311-cp311-win32.whl", hash = "sha256:731dc15b385ac52289743d476245b61e1a2927e803bef655b52bc3b2a75a21f3", size = 222300, upload-time = "2026-05-10T18:00:08.608Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f0/a71ddbd874431e7a7cd96071f0c331cfbbad07704833c765d24ffbab8a67/coverage-7.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfb0ed8ec5d25e93face268115d7964db9df8b9aae8edcde9ec6b16c726a7cc1", size = 223241, upload-time = "2026-05-10T18:00:10.746Z" }, + { url = "https://files.pythonhosted.org/packages/d8/6e/d9d312a5151a96cd110efee32efc3fc97b01ebd86203fe618ccb29cf4c92/coverage-7.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:7ebb1c6df9f78046a1b1e0a89674cd4bf73b7c648914eebcf976a57fd99a5627", size = 221908, upload-time = "2026-05-10T18:00:12.242Z" }, + { url = "https://files.pythonhosted.org/packages/09/1e/2f996b2c8415cbb6f54b0f5ec1ee850c96d7911961afb4fc05f4a89d8c58/coverage-7.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7ffd19fc8aed057fd686a17a4935eef5f9859d69208f96310e893e64b9b6ccf5", size = 219967, upload-time = "2026-05-10T18:00:13.756Z" }, + { url = "https://files.pythonhosted.org/packages/34/23/35c7aea1274aef7525bdd2dc92f710bdde6d11652239d71d1ec450067939/coverage-7.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:829994cfe1aeb773ca27bf246d4badc1e764893e3bfb98fff820fcecd1ca4662", size = 220329, upload-time = "2026-05-10T18:00:15.264Z" }, + { url = "https://files.pythonhosted.org/packages/75/cf/a8f4b43a16e194b0261257ad28ded5853ec052570afef4a84e1d81189f3b/coverage-7.14.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b4f07cf7edcb7ec39431a5074d7ea83b29a9f71fcfc494f0f40af4e65180420f", size = 251839, upload-time = "2026-05-10T18:00:17.16Z" }, + { url = "https://files.pythonhosted.org/packages/69/ff/6699e7b71e60d3049eb2bdcbc95ee3f35707b2b0e48f32e9e63d3ce30c08/coverage-7.14.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca3d9cf2c32b521bd9518385608787fa86f38daf993695307531822c3430ed67", size = 254576, upload-time = "2026-05-10T18:00:18.829Z" }, + { url = "https://files.pythonhosted.org/packages/22/ec/c936d495fcd67f48f03a9c4ad3297ff80d1f222a5df3980f15b34c186c21/coverage-7.14.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92af52828e7f29d827346b0294e5a0853fa206db77db0395b282918d41e28db9", size = 255690, upload-time = "2026-05-10T18:00:20.648Z" }, + { url = "https://files.pythonhosted.org/packages/5c/42/5af63f636cc62a4a2b1b3ba9146f6ee6f53a35a50d5cefc54d5670f60999/coverage-7.14.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b2bb6c9d7e769360d0f20a0f219603fd64f0c8f97de17ab25853261602be0fb", size = 257949, upload-time = "2026-05-10T18:00:22.28Z" }, + { url = "https://files.pythonhosted.org/packages/26/d3/a225317bd2012132a27e1176d51660b826f99bb975876463c44ea0d7ee5a/coverage-7.14.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1c9ed6ef99f88fb8c14aa8e2bf8eb0fe55fa2edfea68f8675d78741df1a5ac0e", size = 252242, upload-time = "2026-05-10T18:00:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7f/9e65495298c3ea414742998539c37d048b5e81cc818fb1828cc6b51d10bf/coverage-7.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8231ade007f37959fbf58acc677f26b922c02eda6f0428ea307da0fd39681bf3", size = 253608, upload-time = "2026-05-10T18:00:25.588Z" }, + { url = "https://files.pythonhosted.org/packages/94/46/1522b524a35bdad22b2b8c4f9d32d0a104b524726ec380b2db68db1746f5/coverage-7.14.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d8b013632cc1ce1d09dbe4f32667b4d320ec2f54fc326ebeffcd0b0bcc2bb6c4", size = 251753, upload-time = "2026-05-10T18:00:27.104Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e9/cdf00d38817742c541ade405e115a3f7bf36e6f2a8b99d4f209861b85a2d/coverage-7.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1733198802d71ec4c524f322e2867ee05c62e9e75df86bdca545407a221827d1", size = 255823, upload-time = "2026-05-10T18:00:29.038Z" }, + { url = "https://files.pythonhosted.org/packages/38/fc/5e7877cf5f902d08a17ff1c532511476d87e1bea355bd5028cb97f902e79/coverage-7.14.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:72a305291fa8ee01332f1aaf38b348ca34097f6aa0b0ef627eef2837e57bbba5", size = 251323, upload-time = "2026-05-10T18:00:30.647Z" }, + { url = "https://files.pythonhosted.org/packages/18/9d/50f05a72dff8487464fdd4178dda5daed642a060e60afb644e3d45123559/coverage-7.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fcaba850dd317c65423a9d63d88f9573c53b00354d6dd95724576cc98a131595", size = 253197, upload-time = "2026-05-10T18:00:32.211Z" }, + { url = "https://files.pythonhosted.org/packages/00/3f/6f61ffe6439df266c3cf60f5c99cfaa21103d0210d706a42fc6c30683ff8/coverage-7.14.0-cp312-cp312-win32.whl", hash = "sha256:5ac83957a80d0701310e96d8bec68cdcf4f90a7674b7d13f15a344315b41ab27", size = 222515, upload-time = "2026-05-10T18:00:33.717Z" }, + { url = "https://files.pythonhosted.org/packages/85/19/93853133df2cb371083285ef6a93982a0173e7a233b0f61373ba9fd30eb2/coverage-7.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:70390b0da32cb90b501953716302906e8bcce087cb283e70d8c97729f22e92b2", size = 223324, upload-time = "2026-05-10T18:00:35.172Z" }, + { url = "https://files.pythonhosted.org/packages/74/18/9f7fe62f659f24b7a82a0be56bf94c1bd0a89e0ae7ab4c668f6e82404294/coverage-7.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:91b993743d959b8be85b4abf9d5478216a69329c321efe5be0433c1a841d691d", size = 221944, upload-time = "2026-05-10T18:00:37.014Z" }, + { url = "https://files.pythonhosted.org/packages/6b/76/b7c66ee3c66e1b0f9d894c8125983aa0c03fb2336f2fd16559f9c966157f/coverage-7.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f2bbb8254370eb4c628ff3d6fa8a7f74ddc40565394d4f7ab791d1fe568e37ef", size = 219990, upload-time = "2026-05-10T18:00:38.887Z" }, + { url = "https://files.pythonhosted.org/packages/b3/af/e567cbad5ba69c013a50146dfa886dc7193361fda77521f51274ff620e1b/coverage-7.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23b81107f46d3f21d0cbce30664fcec0f5d9f585638a67081750f99738f6bf66", size = 220365, upload-time = "2026-05-10T18:00:40.864Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/9ad575d505b4d805b254febc8a5b338a2efe278f8786e56ff1cb8413f9c3/coverage-7.14.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:22a7e06a5f11a757cdfe79018e9095f9f69ae283c5cd8123774c788deec8717b", size = 251363, upload-time = "2026-05-10T18:00:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5f/b5370068b2f57787454592ed7dcd1002f0f1703b7db1fa30f6a325a4ca6e/coverage-7.14.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9d1aa57a1dc8e05bdc42e81c5d671d849577aeedf279f4c449d6d286f9ed88ca", size = 253961, upload-time = "2026-05-10T18:00:44.079Z" }, + { url = "https://files.pythonhosted.org/packages/29/1e/51adf17738976e8f2b85ddef7b7aa12a0838b056c92f175941d8862767c1/coverage-7.14.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90c1a51bcfddf645b3bb7ec333d9e94393a8e94f55642380fa8a9a5a9e636cb7", size = 255193, upload-time = "2026-05-10T18:00:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/7b/5bfd7ac1df3b881c2ac7a5cbc99c7609e6296c402f5ef587cd81c6f355b3/coverage-7.14.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a841fae2fadcae4f438d43b6ccc4aac2ad609f47cdb6cfdce60cbb3fe5ca7bc2", size = 257326, upload-time = "2026-05-10T18:00:47.173Z" }, + { url = "https://files.pythonhosted.org/packages/7d/38/1d37d316b174fad3843a1d76dbdfe4398771c9ecd0515935dd9ece9cd627/coverage-7.14.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c79d2319cabef1fe8e86df73371126931550804738f78ad7d31e3aad85a67367", size = 251582, upload-time = "2026-05-10T18:00:49.152Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/746704f95980ba220214e1a41e18cec5aea80a898eaa53c51bf2d645ff36/coverage-7.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b23b0c6f0b1db6ad769b7050c8b641c0bf215ded26c1816955b17b7f26edfa9", size = 253325, upload-time = "2026-05-10T18:00:51.252Z" }, + { url = "https://files.pythonhosted.org/packages/e1/b9/bbe87206d9687b192352f893797825b5f5b15ecd3aa9c68fbff0c074d77b/coverage-7.14.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:55d3089079ce181a4566b1065ab28d2575eb76d8ac8f81f4fcda2bf037fee087", size = 251291, upload-time = "2026-05-10T18:00:52.816Z" }, + { url = "https://files.pythonhosted.org/packages/46/57/b8cdb12ac0d73ef0243218bd5e22c9df8f92edab8018213a86aec67c5324/coverage-7.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:49c005cba1e2f9677fb2845dcdf9a2e72a52a17d63e8231aaaae35d9f50215ef", size = 255448, upload-time = "2026-05-10T18:00:54.548Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d4/5002019538b2036ce3c84340f54d2fd5100d55b0a6b0894eee56128d03c7/coverage-7.14.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9117377b823daa28aa8635fbb08cda1cd6be3d7143257345459559aeef852d52", size = 251110, upload-time = "2026-05-10T18:00:56.122Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/20c5009477660f084e6ed60bc02a91894b8e234e617e86ecfd9aaf78e27b/coverage-7.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7b79d646cf46d5cf9a9f40281d4441df5849e445726e369006d2b117710b33fe", size = 252885, upload-time = "2026-05-10T18:00:57.967Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ab/3cf6427ac9c1f1db747dbb1ce71dde47984876d4c2cfd018a3fef0a78d4d/coverage-7.14.0-cp313-cp313-win32.whl", hash = "sha256:fb609b3658479e33f9516d46f1a89dbb9b6c261366e3a11844a96ec487533dae", size = 222539, upload-time = "2026-05-10T18:00:59.581Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b8/9228523e80321c2cb4880d1f589bc0171f2f71432c35118ad04dc01decce/coverage-7.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0773d8329cf32b6fd222e4b52622c61fe8d503eb966cfc8d3c3c10c96266d50e", size = 223344, upload-time = "2026-05-10T18:01:01.531Z" }, + { url = "https://files.pythonhosted.org/packages/a3/99/118daa192f95e3a6cb2740100fbf8797cda1734b4134ef0b5d501a7fa8f3/coverage-7.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:b4e26a0f1b696faf283bffe5b8569e44e336c582439df5d53281ab89ee0cba96", size = 221966, upload-time = "2026-05-10T18:01:03.16Z" }, + { url = "https://files.pythonhosted.org/packages/e6/f1/a46cc0c013be170216253184a32366d7cbdb9252feaec866b05c2d12a894/coverage-7.14.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:953f521ca9445300397e65fda3dca58b2dbd68fee983777420b57ac3c77e9f90", size = 220679, upload-time = "2026-05-10T18:01:05.058Z" }, + { url = "https://files.pythonhosted.org/packages/64/8c/9c30a3d311a34177fa432995be7fbfc64477d8bac5630bd38055b1c9b424/coverage-7.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:98af83fd65ae24b1fdd03aaead967a9f523bcd2f1aab2d4f3ffda65bb568a6f1", size = 221033, upload-time = "2026-05-10T18:01:07.002Z" }, + { url = "https://files.pythonhosted.org/packages/9a/cd/3fb5e06c3badefd0c1b47e2044fdca67f8220a4ec2e7fcfb476aa0a67c6c/coverage-7.14.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:668b92e6958c4db7cf92e81caac328dfbbdbb215db2850ad28f0cbe1eea0bfbd", size = 262333, upload-time = "2026-05-10T18:01:08.903Z" }, + { url = "https://files.pythonhosted.org/packages/a8/e6/fbc322325c7294d3e22c1ad6b79e45d0806b25228c8e5842aed6d8169aa7/coverage-7.14.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9fbd898551762dea00d3fef2b1c4f99afd2c6a3ff952ea07d60a9bd5ed4f34bc", size = 264410, upload-time = "2026-05-10T18:01:10.531Z" }, + { url = "https://files.pythonhosted.org/packages/08/92/c497b264bec1673c47cc77e26f760fcda4654cabf1f39546d1a23a3b8c35/coverage-7.14.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68af363c07ecd8d4b7d4043d85cb376d7d227eceb54e5323ee45da73dbd3e426", size = 266836, upload-time = "2026-05-10T18:01:12.19Z" }, + { url = "https://files.pythonhosted.org/packages/78/fc/045da320987f401af5d2815d351e8aa799aec859f60e29f445e3089eeedb/coverage-7.14.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e57054a583da8ac55edf24117ea4c9133032cfc4cf72aa2d48c1e5d4b52f899", size = 267974, upload-time = "2026-05-10T18:01:13.926Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ae/227b1e379497fb7a4fc3286e620f80c8a1e7cec66d45695a01639eb1af65/coverage-7.14.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc3499459bbcdd51a65b64c35ab7ed2764eaf3cba826e0df3f1d7fe2e102b70b", size = 261578, upload-time = "2026-05-10T18:01:15.564Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f5/3570342900f2acea31d33ff1590c5d8bac1a8e1a2e1c6d34a5d5e61de681/coverage-7.14.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:45899ec2138a4346ed34d601dedf5076fb74edf2d1dd9dc76a78e82397edee90", size = 264394, upload-time = "2026-05-10T18:01:17.607Z" }, + { url = "https://files.pythonhosted.org/packages/16/29/de1bbc01c935b28f89b1dc3db85b011c055e843a8e5e3b83141c3f80af7f/coverage-7.14.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8767486808c436f05b23ab98eb963fb29185e32a9357a166971685cb3459900f", size = 262022, upload-time = "2026-05-10T18:01:19.304Z" }, + { url = "https://files.pythonhosted.org/packages/35/95/f53890b0bf2fc10ab168e05d38869215e73ca24c4cb521c3bb0eb62fe16b/coverage-7.14.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a3b5ddfd6aa7ddad53ee3edb231e88a2151507a43229b7d71b953916deca127d", size = 265732, upload-time = "2026-05-10T18:01:21.494Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ea/c919e259081dd2bdf0e43b87209709ba7ec2e4117c2a7f5185379c43463c/coverage-7.14.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:63df0fe568e698e1045792399f8ab6da3a6c2dce3182813fb92afa2641087b47", size = 260921, upload-time = "2026-05-10T18:01:23.533Z" }, + { url = "https://files.pythonhosted.org/packages/1a/2c/c2831889705a81dc5d1c6ca12e4d8e9b95dfc146d153488a6c0ea685d28e/coverage-7.14.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:827d6397dbd95144939b18f89edf31f63e1f99633e8d5f32f22ba8bdda567477", size = 263109, upload-time = "2026-05-10T18:01:25.165Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a9/2fcae5003cac3d63fe344d2166243c2756935f48420863c5272b240d550b/coverage-7.14.0-cp313-cp313t-win32.whl", hash = "sha256:7bf43e000d24012599b879791cff41589af90674722421ef11b11a5431920bab", size = 223212, upload-time = "2026-05-10T18:01:27.157Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bb/18e94d7b14b9b398164197114a587a04ab7c9fdbe1d237eef57311c5e883/coverage-7.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3f5549365af25d770e06b1f8f5682d9a5637d06eb494db91c6fa75d3950cc917", size = 224272, upload-time = "2026-05-10T18:01:29.107Z" }, + { url = "https://files.pythonhosted.org/packages/db/56/4f14fad782b035c81c4ffd09159e7103d42bb1d93ac8496d04b90a11b7da/coverage-7.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6d160217ec6fe890f16ad3a9531761589443749e448f91986c972714fad361c8", size = 222530, upload-time = "2026-05-10T18:01:31.151Z" }, + { url = "https://files.pythonhosted.org/packages/1c/18/b9a6586d73992807c26f9a5f274131be3d76b56b18a82b9392e2a25d2e45/coverage-7.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9aed9fa983514ca032790f3fe0d1c0e42ca7e16b42432af1706b50a9a46bef5d", size = 220036, upload-time = "2026-05-10T18:01:33.057Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9b/4165a1d56ddc302a0e2d518fd9d412a4fd0b57562618c78c5f21c57194f5/coverage-7.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ba3b8390db29296dbbf49e91b6fe08f990743a90c8f447ba4c2ffc29670dfa63", size = 220368, upload-time = "2026-05-10T18:01:34.705Z" }, + { url = "https://files.pythonhosted.org/packages/69/aa/c12e52a5ba148d9995229d557e3be6e554fe469addc0e9241b2f0956d8ea/coverage-7.14.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3a5d8e876dfa2f102e970b183863d6dedd023d3c0eeca1fe7a9787bc5f28b212", size = 251417, upload-time = "2026-05-10T18:01:36.949Z" }, + { url = "https://files.pythonhosted.org/packages/d7/51/ec641c26e6dca1b25a7d2035ba6ecb7c884ef1a100a9e42fbe4ce4405139/coverage-7.14.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ebb8f4614a3787d567e610bbfdf96a4798dd69a1afb1bd8ad228d4111fe6ff3", size = 253924, upload-time = "2026-05-10T18:01:38.985Z" }, + { url = "https://files.pythonhosted.org/packages/33/c4/59c3de0bd1b538824173fd518fed51c1ce740ca5ed68e74545983f4053a9/coverage-7.14.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b9bf47223dd8db3d4c4b2e443b02bace480d428f0822c3f991600448a176c97", size = 255269, upload-time = "2026-05-10T18:01:40.957Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a9/36dfa153a62040296f6e7febfdb20a5720622f6ef5a81a41e8237b9a5344/coverage-7.14.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3485a836550b303d006d57cc06e3d5afaabc642c77050b7c985a97b13e3776b8", size = 257583, upload-time = "2026-05-10T18:01:42.607Z" }, + { url = "https://files.pythonhosted.org/packages/26/7b/cc2c048d4114d9ab1c2409e9ee365e5ae10736df6dffcfc9444effa6c708/coverage-7.14.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e7e88110bae996d199d1693ca8ec3fd52441d426401ae963437598667b4c5eb", size = 251434, upload-time = "2026-05-10T18:01:44.537Z" }, + { url = "https://files.pythonhosted.org/packages/ee/df/6770eaa576e604575e9a78055313250faef5faa84bd6f71a39fece519c43/coverage-7.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15228a6800ce7bdf1b74800595e56db7138cecb338fdbf044806e10dcf182dfe", size = 253280, upload-time = "2026-05-10T18:01:46.175Z" }, + { url = "https://files.pythonhosted.org/packages/ad/9e/1c0264514a3f98259a6d64765a397b2c8373e3ba59ee722a4802d3ec0c61/coverage-7.14.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9d26ac7f5398bafc5b57421ad994e8a4749e8a7a0e62d05ec7d53014d5963bfa", size = 251241, upload-time = "2026-05-10T18:01:48.732Z" }, + { url = "https://files.pythonhosted.org/packages/64/16/4efdf3e3c4079cdbf0ece56a2fea872df9e8a3e15a13a0af4400e1075944/coverage-7.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb73254ff43c911c967a899e1359bc5049b4b115d6e8fbdde4937d0a2246cd5", size = 255516, upload-time = "2026-05-10T18:01:50.819Z" }, + { url = "https://files.pythonhosted.org/packages/93/69/b1de96346603881b3d1bc8d6447c83200e1c9700ffbaff926ba01ff5724c/coverage-7.14.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:454a380af72c6adada298ed270d38c7a391288198dbfb8467f786f588751a90c", size = 251059, upload-time = "2026-05-10T18:01:52.773Z" }, + { url = "https://files.pythonhosted.org/packages/a4/66/2881853e0363a5e0a724d1103e53650795367471b6afb234f8b49e713bc6/coverage-7.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:65c86fb646d2bd2972e96bd1a8b45817ed907cee68655d6295fe7ec031d04cca", size = 252716, upload-time = "2026-05-10T18:01:54.506Z" }, + { url = "https://files.pythonhosted.org/packages/55/5c/0d3305d002c41dcde873dbe456491e663dc55152ca526b630b5c47efd62f/coverage-7.14.0-cp314-cp314-win32.whl", hash = "sha256:6a6516b02a6101398e19a3f44820f69bab2590697f7def4331f668b14adaf828", size = 222788, upload-time = "2026-05-10T18:01:56.487Z" }, + { url = "https://files.pythonhosted.org/packages/f9/58/6e1b8f52fdc3184b47dc5037f5070d83a3d11042db1594b02d2a44d786c8/coverage-7.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:45e0f79d8351fa76e256716df91eab12890d32678b9590df7ae1042e4bd4cf5d", size = 223600, upload-time = "2026-05-10T18:01:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/00/70/a18c408e674bc26281cadaedc7351f929bd2094e191e4b15271c30b084cc/coverage-7.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:4b899594a8b2d81e5cc064a0d7f9cac2081fed91049456cae7676787e41549c9", size = 222168, upload-time = "2026-05-10T18:02:00.411Z" }, + { url = "https://files.pythonhosted.org/packages/3d/89/2681f071d238b62aff8dfc2ab44fc24cfdb38d1c01f391a80522ff5d3a16/coverage-7.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f580f8c80acd94ac72e863efe2cab791d8c38d153e0b463b92dfa000d5c84cd1", size = 220766, upload-time = "2026-05-10T18:02:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c7/c987babafd9207ffa1995e1ef1f9b26762cf4963aa768a66b6f0501e4616/coverage-7.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a2bd259c442cd43c49b30fbafc51776eb19ea396faf159d26a83e6a0a5f13b0c", size = 221035, upload-time = "2026-05-10T18:02:04.017Z" }, + { url = "https://files.pythonhosted.org/packages/5a/e9/d6a5ac3b333088143d6fc877d398a9a674dc03124a2f776e131f03864823/coverage-7.14.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a706b908dfa85538863504c624b237a3cc34232bf403c057414ebfdb3b4d9f84", size = 262405, upload-time = "2026-05-10T18:02:05.915Z" }, + { url = "https://files.pythonhosted.org/packages/38/b1/e70838d29a7c08e22d44398a46db90815bbcbf28de06992bd9210d1a8d8e/coverage-7.14.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7333cd944ee4393b9b3d3c1b598c936d4fc8d70573a4c7dacfec5590dd50e436", size = 264530, upload-time = "2026-05-10T18:02:07.582Z" }, + { url = "https://files.pythonhosted.org/packages/6b/73/5c31ef97763288d03d9995152b96d5475b527c63d91c84b01caea894b83a/coverage-7.14.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f162bc9a15b82d947b02651b0c7e1609d6f7a8735ca330cfadec8481dd97d5a", size = 266932, upload-time = "2026-05-10T18:02:09.401Z" }, + { url = "https://files.pythonhosted.org/packages/e1/76/dd56d80f29c5f05b4d76f7e7c6d47cafacae017189c75c5759d24f9ff0cc/coverage-7.14.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:362cb78e01a5dc82009d88004cf60f2e6b6d6fcbfdec05b05af73b0abf40118f", size = 268062, upload-time = "2026-05-10T18:02:11.399Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c7/27ba85cd5b95614f159ff93ebff1901584a8d192e2e5e24c4943a7453f59/coverage-7.14.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:acebd068fca5512c3a6fde9c045f901613478781a73f0e82b307b214daef23fb", size = 261504, upload-time = "2026-05-10T18:02:13.257Z" }, + { url = "https://files.pythonhosted.org/packages/13/2e/e8149f60ab5d5684c6eee881bdf34b127115cddbb958b196768dd9d63473/coverage-7.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:29fe3da551dface75deb2ccbf87b6b66e2e7ef38f6d89050b428be94afff3490", size = 264398, upload-time = "2026-05-10T18:02:15.063Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7f/1261b025285323225f4b4abffa5a643649dfd67e25ddca7ebcbdea3b7cb3/coverage-7.14.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b4cc4fce8672fffcb09b0eafc167b396b3ba53c4a7230f54b7aaffbf6c835fa9", size = 262000, upload-time = "2026-05-10T18:02:16.756Z" }, + { url = "https://files.pythonhosted.org/packages/d3/dc/829c54f60b9d08389439c00f813c752781c496fc5788c78d8006db4b4f2b/coverage-7.14.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5d4a51aad8ba8bdcd2b8bd8f03d4aca19693fa2327a3470e4718a25b03481020", size = 265732, upload-time = "2026-05-10T18:02:18.817Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b0/70bd1419941652fa062689cba9c3eeafb8f5e6fbb890bce41c3bdda5dbd6/coverage-7.14.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9f323af3e1e4f68b60b7b247e37b8515563a61375518fa59de1af48ba28a3db6", size = 260847, upload-time = "2026-05-10T18:02:20.528Z" }, + { url = "https://files.pythonhosted.org/packages/f2/73/be40b2390656c654d35ea0015ea7ba3d945769cf80790ad5e0bb2d56d2ba/coverage-7.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1a0abc7342ea9711c469dd8b821c6c311e6bc6aac1442e5fbd6b27fae0a8f3db", size = 263166, upload-time = "2026-05-10T18:02:22.337Z" }, + { url = "https://files.pythonhosted.org/packages/29/55/4a643f712fcf7cf2881f8ec1e0ccb7b164aff3108f69b51801246c8799f2/coverage-7.14.0-cp314-cp314t-win32.whl", hash = "sha256:a9f864ef57b7172e2db87a096642dd51e179e085ab6b2c371c29e885f65c8fb2", size = 223573, upload-time = "2026-05-10T18:02:24.11Z" }, + { url = "https://files.pythonhosted.org/packages/27/96/3acae5da0953be042c0b4dea6d6789d2f080701c77b88e44d5bd41b9219b/coverage-7.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29943e552fdc08e082eb51400fb2f58e118a83b5542bd06531214e084399b644", size = 224680, upload-time = "2026-05-10T18:02:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/93/3d/6ab5d2dd8325d838737c6f8d83d62eb6230e0d70b87b51b57bbfd08fa767/coverage-7.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:742a73ea621953b012f2c4c2219b512180dd84489acf5b1596b0aafc55b9100b", size = 222703, upload-time = "2026-05-10T18:02:27.822Z" }, + { url = "https://files.pythonhosted.org/packages/61/e8/cb8e80d6f9f55b99588625062822bf946cf03ed06315df4bd8397f5632a1/coverage-7.14.0-py3-none-any.whl", hash = "sha256:8de5b61163aee3d05c8a2beab6f47913df7981dad1baf82c414d99158c286ab1", size = 211764, upload-time = "2026-05-10T18:02:29.538Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, + { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "13.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, + { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, + { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/d0/c177e29701cf1d3008d7d2b16b5fc626592ce13bd535f8795c5f57187e0e/cuda_pathfinder-1.5.4-py3-none-any.whl", hash = "sha256:9563d3175ce1828531acf4b94e1c1c7d67208c347ca002493e2654878b26f4b7", size = 51657, upload-time = "2026-04-27T22:42:07.712Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] + +[[package]] +name = "cyclopts" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docstring-parser" }, + { name = "rich" }, + { name = "rich-rst" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/fb/d953a011e5768b808cec4ab845b6443de3acbea47061d89eb54220a6d559/cyclopts-4.13.0.tar.gz", hash = "sha256:9539dc298d2e7c229f22402e67e47210e4aa7bbbf7c60024b617d7e0340f68b7", size = 177280, upload-time = "2026-05-16T23:51:04.247Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/63/932d51674d5d96e818e41e4c2dfb68addccd4588e4b8e3d7fe46132e7e6c/cyclopts-4.13.0-py3-none-any.whl", hash = "sha256:b2e55e6bb51c33c78f5df23f8c8e0caa3df7e68639d2fb0cf8aa451a7d58b3e4", size = 214930, upload-time = "2026-05-16T23:51:05.86Z" }, +] + +[[package]] +name = "datasets" +version = "4.8.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/34/14cd8e76f907f7d4dca2334cfeec9f81d30fd15c25a015f99aaea694eaed/datasets-4.8.5.tar.gz", hash = "sha256:0f0c1c3d56ffff2c93b2f4c63c95bac94f3d7e8621aea2a2a576275233bba772", size = 605649, upload-time = "2026-04-27T15:43:57.384Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/99/00f3196036501b53032c4b1ab8337a0b978dee832ed276dae3815df4e8b5/datasets-4.8.5-py3-none-any.whl", hash = "sha256:5079900781719c0e063a8efdd2cd95a31ad0c63209178669cd23cf1b926149ff", size = 528973, upload-time = "2026-04-27T15:43:53.702Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, +] + +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, +] + +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + +[[package]] +name = "exceptiongroup" +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/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/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]] +name = "fastapi" +version = "0.136.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/45/c130091c2dfa061bbfe3150f2a5091ef1adf149f2a8d2ae769ecaf6e99a2/fastapi-0.136.1.tar.gz", hash = "sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f", size = 397448, upload-time = "2026-04-23T16:49:44.046Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/ff/2e4eca3ade2c22fe1dea7043b8ee9dabe47753349eb1b56a202de8af6349/fastapi-0.136.1-py3-none-any.whl", hash = "sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f", size = 117683, upload-time = "2026-04-23T16:49:42.437Z" }, +] + +[[package]] +name = "fastmcp" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastmcp-slim", extra = ["client", "server"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/a9/5c5a01b6abd5346bf60b97cfd29e4a86661940c27dd562bfcda07fd03519/fastmcp-3.3.1.tar.gz", hash = "sha256:979362ea557de42a5f40342563c7e4b236bcc8e7cd192715f50030695d1a71cd", size = 28681699, upload-time = "2026-05-15T15:50:39.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/11/6b1bdada6ccfe647d615ae63f9106f8136aec17971e9361546af01c7d38e/fastmcp-3.3.1-py3-none-any.whl", hash = "sha256:862440c5c4d281363a5995eee59d77f0f7cac1f18869038729cecf03b02fc522", size = 7903, upload-time = "2026-05-15T15:50:36.424Z" }, +] + +[[package]] +name = "fastmcp-slim" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pydantic", extra = ["email"] }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/a0/627103e517e1d0d6f1eec633d5662d13e776f01b45ad188e4f5f7478b438/fastmcp_slim-3.3.1.tar.gz", hash = "sha256:0957835fc59452e143ab2f4b7836d2d2df9b2d9958408edc79ba8b56232b2a88", size = 567007, upload-time = "2026-05-15T15:50:10.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/ee/97047f4cc2d7b1d46670d08d8ad01a96e7a748cc01c0b4b351ad8eddbc7a/fastmcp_slim-3.3.1-py3-none-any.whl", hash = "sha256:6cf1c2d77e3adb0d409d6825ed6b0b2a999062973e00b8eea03bd48bf9b4c043", size = 738644, upload-time = "2026-05-15T15:50:08.336Z" }, +] + +[package.optional-dependencies] +client = [ + { name = "authlib" }, + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "mcp" }, + { name = "opentelemetry-api" }, + { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, +] +server = [ + { name = "authlib" }, + { name = "cyclopts" }, + { name = "exceptiongroup" }, + { name = "griffelib" }, + { name = "httpx" }, + { name = "jsonref" }, + { name = "jsonschema-path" }, + { name = "mcp" }, + { name = "openapi-pydantic" }, + { name = "opentelemetry-api" }, + { name = "packaging" }, + { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, + { name = "pyperclip" }, + { name = "python-multipart" }, + { name = "pyyaml" }, + { name = "uncalled-for" }, + { name = "uvicorn" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gradio" +version = "6.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "audioop-lts", marker = "python_full_version >= '3.13'" }, + { name = "brotli" }, + { name = "fastapi" }, + { name = "gradio-client" }, + { name = "groovy" }, + { name = "hf-gradio" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "orjson" }, + { name = "packaging" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "pydub" }, + { name = "python-multipart" }, + { name = "pytz" }, + { name = "pyyaml" }, + { name = "safehttpx" }, + { name = "semantic-version" }, + { name = "starlette" }, + { name = "tomlkit" }, + { name = "typer" }, + { name = "typing-extensions" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/bd/7d1544571de4566138e50c868b91bb79e38c998266896d38fed3d3a77898/gradio-6.14.0.tar.gz", hash = "sha256:4972ef7d01ac57472772624eb4e095767b6c8f3cd4846b7fea648e8034cda9f8", size = 36026409, upload-time = "2026-04-30T16:50:31.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/e2/41f991b2e212b7afda3a9927676ebf8af302e5a2c632b330bd70bf2cf2c1/gradio-6.14.0-py3-none-any.whl", hash = "sha256:bb702f5ab643510d167bae54269ad6e985c2185174d388fe542cc5957f51f4fd", size = 19687959, upload-time = "2026-04-30T16:50:26.914Z" }, +] + +[[package]] +name = "gradio-client" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "packaging" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/e6/6b6029f5fe2ad7f1211105d530e34d991014c2cae463f9223033031cfc4f/gradio_client-2.5.0.tar.gz", hash = "sha256:4cde99bad62149595c30c90876ca2e405e3a13687ecf895474f3412cb476673d", size = 59013, upload-time = "2026-04-20T23:16:21.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/81/0a861b8e1ff42960139c6cd4c7dd591292fa09ea1ae2d87677441cba4c00/gradio_client-2.5.0-py3-none-any.whl", hash = "sha256:d43e2179c29076292a76485ad7ed2e6eaa19d14ac58283bd7f5beabfe4ca958c", size = 59952, upload-time = "2026-04-20T23:16:20.186Z" }, +] + +[[package]] +name = "griffelib" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, +] + +[[package]] +name = "groovy" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/36/bbdede67400277bef33d3ec0e6a31750da972c469f75966b4930c753218f/groovy-0.1.2.tar.gz", hash = "sha256:25c1dc09b3f9d7e292458aa762c6beb96ea037071bf5e917fc81fb78d2231083", size = 17325, upload-time = "2025-02-28T20:24:56.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/27/3d6dcadc8a3214d8522c1e7f6a19554e33659be44546d44a2f7572ac7d2a/groovy-0.1.2-py3-none-any.whl", hash = "sha256:7f7975bab18c729a257a8b1ae9dcd70b7cafb1720481beae47719af57c35fa64", size = 14090, upload-time = "2025-02-28T20:24:55.152Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-gradio" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gradio-client" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/86/c9694b7cfada5780e75769e60dc161a161f4dd7fc91b61db5e3a3338bef9/hf_gradio-0.4.1.tar.gz", hash = "sha256:a017d942618f0d495a58ee4563047fa04bef614c00e0cb789a9a6d0633cffa7b", size = 6560, upload-time = "2026-04-22T14:01:32.334Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/2d/afff2ee87e75d8eb85c92bb8cf0e15b05c23c2ebd8fd8dec781d8601ed7f/hf_gradio-0.4.1-py3-none-any.whl", hash = "sha256:76b8cb8be6abe62d74c1ad2d35b42f0629db89aa9e1a8d033cecfe7c856eeab3", size = 4482, upload-time = "2026-04-17T19:53:31.827Z" }, +] + +[[package]] +name = "hf-sandbox" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "httpx" }, + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/b2/8975aafebf0deb9e137b59c141698dc0e911c68543ca84fd78fc3ffd2edf/hf_sandbox-0.1.1.tar.gz", hash = "sha256:4f4af45a9e0fef33e1d537204b21ce6c09f9f0347d65526be794be81b6987513", size = 5654, upload-time = "2026-05-10T15:48:38.276Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/aa/292aa188bbd16b68a13cbd5229e17ba07ca487ef65290995f337c6935a99/hf_sandbox-0.1.1-py3-none-any.whl", hash = "sha256:3200c89521854486945494b765f5106107cca0a4f3247b01f520e8d657e17446", size = 4906, upload-time = "2026-05-10T15:48:36.592Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/d8/5c06fc76461418326a7decf8367480c35be11a41fd938633929c60a9ec6b/hf_xet-1.5.0.tar.gz", hash = "sha256:e0fb0a34d9f406eed88233e829a67ec016bec5af19e480eac65a233ea289a948", size = 837196, upload-time = "2026-05-06T06:18:15.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/9b/6912c99070915a4f28119e3c5b52a9abd1eec0ad5cb293b8c967a0c6f5a2/hf_xet-1.5.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7d70fe2ce97b9db73b9c9b9c81fe3693640aec83416a966c446afea54acfae3c", size = 4023383, upload-time = "2026-05-06T06:17:53.947Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6d/9563cfde59b5d8128a9c7ec972a087f4c782e4f7bac5a85234edfd5d5e49/hf_xet-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:73a0dae8c71de3b0633a45c73f4a4a5ed09e94b43441d82981a781d4f12baa42", size = 3792751, upload-time = "2026-05-06T06:17:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/07/a5/ed5a0cf35b49a0571af5a8f53416dad1877a718c021c9937c3a53cb45781/hf_xet-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a60290ec57e9b71767fba7c3645ddafdd0759974b540441510c629c6db6db24a", size = 4456058, upload-time = "2026-05-06T06:17:40.735Z" }, + { url = "https://files.pythonhosted.org/packages/60/fb/3ae8bf2a7a37a4197d0195d7247fd25b3952e15cb8a599e285dfaa6f52b3/hf_xet-1.5.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e5de0f6deada0dada870bb376a11bcd1f08abf3a968a6d118f33e72d1b1eb480", size = 4250783, upload-time = "2026-05-06T06:17:38.412Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/8bae40d4d91525085137196e84eb0ed49cf65b5e96e5c3ecdadd8bd0fac2/hf_xet-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c799d49f1a5544a0ef7591c0ee75e0d6b93d6f56dc7a4979f59f7518d2872216", size = 4445594, upload-time = "2026-05-06T06:18:04.219Z" }, + { url = "https://files.pythonhosted.org/packages/13/59/c74efbbd4e8728172b2cc72a2bc014d2947a4b7bdced932fbd3f5da1a4e5/hf_xet-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2baea1b0b989e5c152fe81425f7745ddc8901280ba3d97c98d8cdece7b706c60", size = 4663995, upload-time = "2026-05-06T06:18:06.1Z" }, + { url = "https://files.pythonhosted.org/packages/73/32/8e1e0410af64cda9b139d1dcebdc993a8ff9c8c7c0e2696ae356d75ccc0d/hf_xet-1.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:526345b3ed45f374f6317349df489167606736c876241ba984105afe7fd4839d", size = 3966608, upload-time = "2026-05-06T06:18:19.74Z" }, + { url = "https://files.pythonhosted.org/packages/fc/34/a8febc8f4edbea8b3e21b02ebc8b628679b84ba7e45cde624a7736b51500/hf_xet-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:786d28e2eb8315d5035544b9d137b4a842d600c434bb91bf7d0d953cce906ad4", size = 3796946, upload-time = "2026-05-06T06:18:17.568Z" }, + { url = "https://files.pythonhosted.org/packages/2a/20/8fc8996afe5815fa1a6be8e9e5c02f24500f409d599e905800d498a4e14d/hf_xet-1.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:872d5601e6deea30d15865ede55d29eac6daf5a534ab417b99b6ef6b076dd96c", size = 4023495, upload-time = "2026-05-06T06:18:01.94Z" }, + { url = "https://files.pythonhosted.org/packages/32/6a/93d84463c00cecb561a7508aa6303e35ee2894294eac14245526924415fe/hf_xet-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9929561f5abf4581c8ea79587881dfef6b8abb2a0d8a51915936fc2a614f4e73", size = 3792731, upload-time = "2026-05-06T06:18:00.021Z" }, + { url = "https://files.pythonhosted.org/packages/9d/5a/8ec8e0c863b382d00b3c2e2af6ded6b06371be617144a625903a6d562f4b/hf_xet-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7b7bbae318e583a86fb21e5a4a175d6721d628a2874f4bd022d0e660c32a682", size = 4456738, upload-time = "2026-05-06T06:17:49.574Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ca/f7effa1a67717da2bcc6b6c28f71c6ca648c77acaec4e2c32f40cbe16d85/hf_xet-1.5.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cf7b2dc6f31a4ea754bb50f74cde482dcf5d366d184076d8530b9872787f3761", size = 4251622, upload-time = "2026-05-06T06:17:47.096Z" }, + { url = "https://files.pythonhosted.org/packages/65/f2/19247dba3e231cf77dec59ddfb878f00057635ff773d099c9b59d37812c3/hf_xet-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8dbcbab554c9ef158ef2c991545c3e970ddd8cc7acdcd0a78c5a41095dab4ded", size = 4445667, upload-time = "2026-05-06T06:18:11.983Z" }, + { url = "https://files.pythonhosted.org/packages/7f/64/6f116801a3bcfb6f59f5c251f48cadc47ea54026441c4a385079286a94fa/hf_xet-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5906bf7718d3636dc13402914736abe723492cb730f744834f5f5b67d3a12702", size = 4664619, upload-time = "2026-05-06T06:18:13.771Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e8/069542d37946ed08669b127e1496fa99e78196d71de8d41eda5e9f1b7a58/hf_xet-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f3dc2248fc01cc0a00cd392ab497f1ca373fcbc7e3f2da1f452480b384e839e", size = 3966802, upload-time = "2026-05-06T06:18:28.162Z" }, + { url = "https://files.pythonhosted.org/packages/f9/91/fc6fdec27b14d04e88c386ac0a0129732b53fa23f7c4a78f4b83a039c567/hf_xet-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b285cea1b5bab46b758772716ba8d6854a1a0310fed1c249d678a8b38601e5a0", size = 3797168, upload-time = "2026-05-06T06:18:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fb/69ff198a82cae7eb1a69fb84d93b3a3e4816564d76817fe541ddc96874eb/hf_xet-1.5.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dad0dc84e941b8ba3c860659fe1fdc35c049d47cce293f003287757e971a8f56", size = 4030814, upload-time = "2026-05-06T06:17:57.933Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ff/edcc2b40162bef3ff78e14ab637e5f3b89243d6aee72f5949d3bb6a5af83/hf_xet-1.5.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fd6e5a9b0fdac4ed03ed45ef79254a655b1aaab514a02202617fbf643f5fdf7a", size = 3798444, upload-time = "2026-05-06T06:17:55.79Z" }, + { url = "https://files.pythonhosted.org/packages/49/4d/103f76b04310e5e57656696cc184690d20c466af0bca3ca88f8c8ea5d4f3/hf_xet-1.5.0-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3531b1823a0e6d77d80f9ed15ca0e00f0d115094f8ac033d5cae88f4564cc949", size = 4465986, upload-time = "2026-05-06T06:17:44.886Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a2/546f47f464737b3edbab6f8ddb57f2599b93d2cbb66f06abb475ccb48651/hf_xet-1.5.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9a0ee58cd18d5ea799f7ed11290bbccbe56bdd8b1d97ca74b9cc49a3945d7a3b", size = 4259865, upload-time = "2026-05-06T06:17:42.639Z" }, + { url = "https://files.pythonhosted.org/packages/95/7f/1be593c1f28613be2e196473481cd81bfc5910795e30a34e8f744f6cac4f/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e60df5a42e9bed8628b6416af2cba4cba57ae9f02de226a06b020d98e1aab18", size = 4459835, upload-time = "2026-05-06T06:18:08.026Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b2/703569fc881f3284487e68cda7b42179978480da3c438042a6bbbb4a671c/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4b35549ce62601b84da4ff9b24d970032ace3d4430f52d91bcbb26c901d6c690", size = 4672414, upload-time = "2026-05-06T06:18:09.864Z" }, + { url = "https://files.pythonhosted.org/packages/af/37/1b6def445c567286b50aa3b33828158e135b1be44938dde59f11382a500c/hf_xet-1.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:2806c7c17b4d23f8d88f7c4814f838c3b6150773fe339c20af23e1cfaf2797e4", size = 3977238, upload-time = "2026-05-06T06:18:23.621Z" }, + { url = "https://files.pythonhosted.org/packages/62/94/3b66b148778ee100dcfd69c2ca22b57b41b44d3063ceec934f209e9184ce/hf_xet-1.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:b6c9df403040248c76d808d3e047d64db2d923bae593eb244c41e425cf6cd7be", size = 3806916, upload-time = "2026-05-06T06:18:21.7Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httptools" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" }, + { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" }, + { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" }, + { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" }, + { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" }, + { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" }, + { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" }, + { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, + { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, + { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, + { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, + { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, + { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, + { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, + { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, + { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/b6/e22bd20a25299c34b8c5922c1545a6320825b13906eb0f7298edfd034a0b/huggingface_hub-1.15.0.tar.gz", hash = "sha256:28abfdddda3927fd4de6a63cf26ab012498a2c24dae52baf150c5c6edf98a1d5", size = 784100, upload-time = "2026-05-15T11:42:52.149Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/11/0b64cc9024329b76d7547c19a67604a61d21d3ba678a69d1b220c29d5112/huggingface_hub-1.15.0-py3-none-any.whl", hash = "sha256:a4a59af04cbc41a3fe3fec429b171ef994ef8c971eda10136746f408dd4e3744", size = 663602, upload-time = "2026-05-15T11:42:50.487Z" }, +] + +[[package]] +name = "idna" +version = "3.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, +] + +[[package]] +name = "jaraco-context" +version = "6.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272, upload-time = "2026-05-15T21:34:10.025Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594, upload-time = "2026-05-15T21:34:08.595Z" }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jiter" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/c1/0cddc6eb17d4c53a99840953f95dd3accdc5cfc7a337b0e9b26476276be9/jiter-0.14.0.tar.gz", hash = "sha256:e8a39e66dac7153cf3f964a12aad515afa8d74938ec5cc0018adcdae5367c79e", size = 165725, upload-time = "2026-04-10T14:28:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/2e/a9959997739c403378d0a4a3a1c4ed80b60aeace216c4d37b303a9fc60a4/jiter-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:02f36a5c700f105ac04a6556fe664a59037a2c200db3b7e88784fac2ddf02531", size = 316927, upload-time = "2026-04-10T14:25:40.753Z" }, + { url = "https://files.pythonhosted.org/packages/27/72/b6de8a531e0adbadd839bec301165feb1fccf00e9ff55073ba2dd20f0043/jiter-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41eab6c09ceffb6f0fe25e214b3068146edb1eda3649ca2aee2a061029c7ba2e", size = 321181, upload-time = "2026-04-10T14:25:42.621Z" }, + { url = "https://files.pythonhosted.org/packages/db/d8/2040b9efa13c917f855c40890ae4119fe02c25b7c7677d5b4fa820a851fc/jiter-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cf4d4c109641f9cfaf4a7b6aebd51654e405cd00fa9ebbf87163b8b97b325aa", size = 347387, upload-time = "2026-04-10T14:25:44.212Z" }, + { url = "https://files.pythonhosted.org/packages/49/62/655c0ad5ce6a8e90f9068c175b8a236877d753e460762b3183c136db1c5b/jiter-0.14.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80c7b41a628e6be2213ad0ece763c5f88aa5ee003fa394d58acaaee1f4b8342", size = 373083, upload-time = "2026-04-10T14:25:45.55Z" }, + { url = "https://files.pythonhosted.org/packages/f1/66/549c40fa068f08710b7570869c306a051eb67a29758bd64f4114f730554c/jiter-0.14.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb3dbf7cc0d4dbe73cce307ebe7eefa7f73a7d3d854dd119ea0c243f03e40927", size = 463639, upload-time = "2026-04-10T14:25:47.452Z" }, + { url = "https://files.pythonhosted.org/packages/25/2f/97a32a05fed14ed58a18e181fdfb619e05163f3726b54ee6080ec0539c09/jiter-0.14.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7054adcdeb06b46efd17b5734f75817a44a2d06d3748e36c3a023a1bb52af9ec", size = 380735, upload-time = "2026-04-10T14:25:49.305Z" }, + { url = "https://files.pythonhosted.org/packages/2a/3b/4347e1d6c2a973d653bbb7a2d671a2d2426e54b52ba735b8ff0d0a29b75c/jiter-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d597cd1bf6790376f3fffc7c708766e57301d99a19314824ea0ccc9c3c70e1e2", size = 358632, upload-time = "2026-04-10T14:25:50.931Z" }, + { url = "https://files.pythonhosted.org/packages/ef/24/ca452fbf2ea33548ed30ce68a39a50442d3f7c9bf0704a7af958a930c057/jiter-0.14.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:df63a14878da754427926281626fd3ee249424a186e25a274e78176d42945264", size = 359969, upload-time = "2026-04-10T14:25:52.381Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a3/94470a0d199287caabeb4da2bb2ae5f6d17f3cf05dfc975d7cb064d58e0f/jiter-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ea73187627bcc5810e085df715e8a99da8bdfd96a7eb36b4b4df700ba6d4c9c", size = 397529, upload-time = "2026-04-10T14:25:53.801Z" }, + { url = "https://files.pythonhosted.org/packages/cf/71/6768edc09d7c45c39f093feb3de105fa718a3e982b5208b8a2ed6382b44b/jiter-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9f541eaf7bb8382367a1a23d6fc3d6aad57f8dd8c18c3c17f838bee20f217220", size = 522342, upload-time = "2026-04-10T14:25:55.396Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6b/5c2e17559a0f4e96e934479f7137df46c939e983fa05244e674815befb73/jiter-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:107465250de4fce00fdb47166bcd51df8e634e049541174fe3c71848e44f52ce", size = 556784, upload-time = "2026-04-10T14:25:56.927Z" }, + { url = "https://files.pythonhosted.org/packages/b1/83/c25f3556a60fc74d11199100f1b6cc0c006b815c8494dea8ca16fe398732/jiter-0.14.0-cp310-cp310-win32.whl", hash = "sha256:ffb2a08a406465bb076b7cc1df41d833106d3cf7905076cc73f0cb90078c7d10", size = 208439, upload-time = "2026-04-10T14:25:58.796Z" }, + { url = "https://files.pythonhosted.org/packages/2e/99/781a1b413f0989b7f2ea203b094b331685f1a35e52e0a45e5d000ecaab27/jiter-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb8b682d10cb0cce7ff4c1af7244af7022c9b01ae16d46c357bdd0df13afb25d", size = 204558, upload-time = "2026-04-10T14:26:00.208Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1f/198ae537fccb7080a0ed655eb56abf64a92f79489dfbf79f40fa34225bcd/jiter-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7e791e247b8044512e070bd1f3633dc08350d32776d2d6e7473309d0edf256a2", size = 316896, upload-time = "2026-04-10T14:26:01.986Z" }, + { url = "https://files.pythonhosted.org/packages/cf/34/da67cff3fce964a36d03c3e365fb0f8726ade2a6cfd4d3c70107e216ead6/jiter-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71527ce13fd5a0c4e40ad37331f8c547177dbb2dd0a93e5278b6a5eecf748804", size = 321085, upload-time = "2026-04-10T14:26:03.364Z" }, + { url = "https://files.pythonhosted.org/packages/ed/36/4c72e67180d4e71a4f5dcf7886d0840e83c49ab11788172177a77570326e/jiter-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c4a7ab56f746014874f2c525584c0daca1dec37f66fd707ecef3b7e5c2228c", size = 347393, upload-time = "2026-04-10T14:26:05.314Z" }, + { url = "https://files.pythonhosted.org/packages/bc/db/9b39e09ceafa9878235c0fc29e3e3f9b12a4c6a98ea3085b998cadf3accc/jiter-0.14.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:376e9dafff914253bb9d46cdc5f7965607fbe7feb0a491c34e35f92b2770702e", size = 372937, upload-time = "2026-04-10T14:26:06.884Z" }, + { url = "https://files.pythonhosted.org/packages/b0/96/0dcba1d7a82c1b720774b48ef239376addbaf30df24c34742ac4a57b67b2/jiter-0.14.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23ad2a7a9da1935575c820428dd8d2490ce4d23189691ce33da1fc0a58e14e1c", size = 463646, upload-time = "2026-04-10T14:26:08.345Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e3/f61b71543e746e6b8b805e7755814fc242715c16f1dba58e1cbccb8032c2/jiter-0.14.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54b3ddf5786bc7732d293bba3411ac637ecfa200a39983166d1df86a59a43c9f", size = 380225, upload-time = "2026-04-10T14:26:10.161Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5e/0ddeb7096aca099114abe36c4921016e8d251e6f35f5890240b31f1f60ae/jiter-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c001d5a646c2a50dc055dd526dad5d5245969e8234d2b1131d0451e81f3a373", size = 358682, upload-time = "2026-04-10T14:26:11.574Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d1/fe0c46cd7fda9cad8f1ff9ad217dc61f1e4280b21052ec6dfe88c1446ef2/jiter-0.14.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:834bb5bdabca2e91592a03d373838a8d0a1b8bbde7077ae6913fd2fc51812d00", size = 359973, upload-time = "2026-04-10T14:26:13.316Z" }, + { url = "https://files.pythonhosted.org/packages/ac/21/f5317f91729b501019184771c80d60abd89907009e7bfa6c7e348c5bdd44/jiter-0.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4e9178be60e229b1b2b0710f61b9e24d1f4f8556985a83ff4c4f95920eea7314", size = 397568, upload-time = "2026-04-10T14:26:15.212Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/79d8f33fb2bf168db0df5c9cd16fe440a8ada57e929d3677b22712c2568f/jiter-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7e4ccff04ec03614e62c613e976a3a5860dc9714ce8266f44328bdc8b1cab2c", size = 522535, upload-time = "2026-04-10T14:26:16.956Z" }, + { url = "https://files.pythonhosted.org/packages/5c/00/d1e3ff3d2a465e67f08507d74bafb2dcd29eba91dc939820e39e8dea38b8/jiter-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:69539d936fb5d55caf6ecd33e2e884de083ff0ea28579780d56c4403094bb8d9", size = 556709, upload-time = "2026-04-10T14:26:18.5Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/bbb2189f62ace8d95e869aa4c84c9946616f301e2d02895a6f20dcc3bba3/jiter-0.14.0-cp311-cp311-win32.whl", hash = "sha256:4927d09b3e572787cc5e0a5318601448e1ab9391bcef95677f5840c2d00eaa6d", size = 208660, upload-time = "2026-04-10T14:26:20.511Z" }, + { url = "https://files.pythonhosted.org/packages/b8/86/c500b53dcbf08575f5963e536ebd757a1f7c568272ba5d180b212c9a87fb/jiter-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:42d6ed359ac49eb922fdd565f209c57340aa06d589c84c8413e42a0f9ae1b842", size = 204659, upload-time = "2026-04-10T14:26:22.152Z" }, + { url = "https://files.pythonhosted.org/packages/75/4a/a676249049d42cb29bef82233e4fe0524d414cbe3606c7a4b311193c2f77/jiter-0.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:6dd689f5f4a5a33747b28686e051095beb214fe28cfda5e9fe58a295a788f593", size = 194772, upload-time = "2026-04-10T14:26:23.458Z" }, + { url = "https://files.pythonhosted.org/packages/5a/68/7390a418f10897da93b158f2d5a8bd0bcd73a0f9ec3bb36917085bb759ef/jiter-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2fb2ce3a7bc331256dfb14cefc34832366bb28a9aca81deaf43bbf2a5659e607", size = 316295, upload-time = "2026-04-10T14:26:24.887Z" }, + { url = "https://files.pythonhosted.org/packages/60/a0/5854ac00ff63551c52c6c89534ec6aba4b93474e7924d64e860b1c94165b/jiter-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5252a7ca23785cef5d02d4ece6077a1b556a410c591b379f82091c3001e14844", size = 315898, upload-time = "2026-04-10T14:26:26.601Z" }, + { url = "https://files.pythonhosted.org/packages/41/a1/4f44832650a16b18e8391f1bf1d6ca4909bc738351826bcc198bba4357f4/jiter-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c409578cbd77c338975670ada777add4efd53379667edf0aceea730cabede6fb", size = 343730, upload-time = "2026-04-10T14:26:28.326Z" }, + { url = "https://files.pythonhosted.org/packages/48/64/a329e9d469f86307203594b1707e11ae51c3348d03bfd514a5f997870012/jiter-0.14.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ede4331a1899d604463369c730dbb961ffdc5312bc7f16c41c2896415b1304a", size = 370102, upload-time = "2026-04-10T14:26:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/94/c1/5e3dfc59635aa4d4c7bd20a820ac1d09b8ed851568356802cf1c08edb3cf/jiter-0.14.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92cd8b6025981a041f5310430310b55b25ca593972c16407af8837d3d7d2ca01", size = 461335, upload-time = "2026-04-10T14:26:31.911Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1b/dd157009dbc058f7b00108f545ccb72a2d56461395c4fc7b9cfdccb00af4/jiter-0.14.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:351bf6eda4e3a7ceb876377840c702e9a3e4ecc4624dbfb2d6463c67ae52637d", size = 378536, upload-time = "2026-04-10T14:26:33.595Z" }, + { url = "https://files.pythonhosted.org/packages/91/78/256013667b7c10b8834f8e6e54cd3e562d4c6e34227a1596addccc05e38c/jiter-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1dcfbeb93d9ecd9ca128bbf8910120367777973fa193fb9a39c31237d8df165", size = 353859, upload-time = "2026-04-10T14:26:35.098Z" }, + { url = "https://files.pythonhosted.org/packages/de/d9/137d65ade9093a409fe80955ce60b12bb753722c986467aeda47faf450ad/jiter-0.14.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ae039aaef8de3f8157ecc1fdd4d85043ac4f57538c245a0afaecb8321ec951c3", size = 357626, upload-time = "2026-04-10T14:26:36.685Z" }, + { url = "https://files.pythonhosted.org/packages/2e/48/76750835b87029342727c1a268bea8878ab988caf81ee4e7b880900eeb5a/jiter-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7d9d51eb96c82a9652933bd769fe6de66877d6eb2b2440e281f2938c51b5643e", size = 393172, upload-time = "2026-04-10T14:26:38.097Z" }, + { url = "https://files.pythonhosted.org/packages/a6/60/456c4e81d5c8045279aefe60e9e483be08793828800a4e64add8fdde7f2a/jiter-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d824ca4148b705970bf4e120924a212fdfca9859a73e42bd7889a63a4ea6bb98", size = 520300, upload-time = "2026-04-10T14:26:39.532Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9f/2020e0984c235f678dced38fe4eec3058cf528e6af36ebf969b410305941/jiter-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff3a6465b3a0f54b1a430f45c3c0ba7d61ceb45cbc3e33f9e1a7f638d690baf3", size = 553059, upload-time = "2026-04-10T14:26:40.991Z" }, + { url = "https://files.pythonhosted.org/packages/ef/32/e2d298e1a22a4bbe6062136d1c7192db7dba003a6975e51d9a9eecabc4c2/jiter-0.14.0-cp312-cp312-win32.whl", hash = "sha256:5dec7c0a3e98d2a3f8a2e67382d0d7c3ac60c69103a4b271da889b4e8bb1e129", size = 206030, upload-time = "2026-04-10T14:26:42.517Z" }, + { url = "https://files.pythonhosted.org/packages/36/ac/96369141b3d8a4a8e4590e983085efe1c436f35c0cda940dd76d942e3e40/jiter-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:fc7e37b4b8bc7e80a63ad6cfa5fc11fab27dbfea4cc4ae644b1ab3f273dc348f", size = 201603, upload-time = "2026-04-10T14:26:44.328Z" }, + { url = "https://files.pythonhosted.org/packages/01/c3/75d847f264647017d7e3052bbcc8b1e24b95fa139c320c5f5066fa7a0bdd/jiter-0.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:ee4a72f12847ef29b072aee9ad5474041ab2924106bdca9fcf5d7d965853e057", size = 191525, upload-time = "2026-04-10T14:26:46Z" }, + { url = "https://files.pythonhosted.org/packages/97/2a/09f70020898507a89279659a1afe3364d57fc1b2c89949081975d135f6f5/jiter-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:af72f204cf4d44258e5b4c1745130ac45ddab0e71a06333b01de660ab4187a94", size = 315502, upload-time = "2026-04-10T14:26:47.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/be/080c96a45cd74f9fce5db4fd68510b88087fb37ffe2541ff73c12db92535/jiter-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4b77da71f6e819be5fbcec11a453fde5b1d0267ef6ed487e2a392fd8e14e4e3a", size = 314870, upload-time = "2026-04-10T14:26:49.149Z" }, + { url = "https://files.pythonhosted.org/packages/7d/5e/2d0fee155826a968a832cc32438de5e2a193292c8721ca70d0b53e58245b/jiter-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f4ea612fe8b84b8b04e51d0e78029ecf3466348e25973f953de6e6a59aa4c1", size = 343406, upload-time = "2026-04-10T14:26:50.762Z" }, + { url = "https://files.pythonhosted.org/packages/70/af/bf9ee0d3a4f8dc0d679fc1337f874fe60cdbf841ebbb304b374e1c9aaceb/jiter-0.14.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62fe2451f8fcc0240261e6a4df18ecbcd58327857e61e625b2393ea3b468aac9", size = 369415, upload-time = "2026-04-10T14:26:52.188Z" }, + { url = "https://files.pythonhosted.org/packages/0f/83/8e8561eadba31f4d3948a5b712fb0447ec71c3560b57a855449e7b8ddc98/jiter-0.14.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6112f26f5afc75bcb475787d29da3aa92f9d09c7858f632f4be6ffe607be82e9", size = 461456, upload-time = "2026-04-10T14:26:53.611Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c9/c5299e826a5fe6108d172b344033f61c69b1bb979dd8d9ddd4278a160971/jiter-0.14.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:215a6cb8fb7dc702aa35d475cc00ddc7f970e5c0b1417fb4b4ac5d82fa2a29db", size = 378488, upload-time = "2026-04-10T14:26:55.211Z" }, + { url = "https://files.pythonhosted.org/packages/5d/37/c16d9d15c0a471b8644b1abe3c82668092a707d9bedcf076f24ff2e380cd/jiter-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ab96a30fb3cb2c7e0cd33f7616c8860da5f5674438988a54ac717caccdbaa", size = 353242, upload-time = "2026-04-10T14:26:56.705Z" }, + { url = "https://files.pythonhosted.org/packages/58/ea/8050cb0dc654e728e1bfacbc0c640772f2181af5dedd13ae70145743a439/jiter-0.14.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3a99c1387b1f2928f799a9de899193484d66206a50e98233b6b088a7f0c1edb2", size = 356823, upload-time = "2026-04-10T14:26:58.281Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/cf71506d270e5f84d97326bf220e47aed9b95e9a4a060758fb07772170ab/jiter-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab18d11074485438695f8d34a1b6da61db9754248f96d51341956607a8f39985", size = 392564, upload-time = "2026-04-10T14:27:00.018Z" }, + { url = "https://files.pythonhosted.org/packages/b0/cc/8c6c74a3efb5bd671bfd14f51e8a73375464ca914b1551bc3b40e26ac2c9/jiter-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:801028dcfc26ac0895e4964cbc0fd62c73be9fd4a7d7b1aaf6e5790033a719b7", size = 520322, upload-time = "2026-04-10T14:27:01.664Z" }, + { url = "https://files.pythonhosted.org/packages/41/24/68d7b883ec959884ddf00d019b2e0e82ba81b167e1253684fa90519ce33c/jiter-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ad425b087aafb4a1c7e1e98a279200743b9aaf30c3e0ba723aec93f061bd9bc8", size = 552619, upload-time = "2026-04-10T14:27:03.316Z" }, + { url = "https://files.pythonhosted.org/packages/b6/89/b1a0985223bbf3150ff9e8f46f98fc9360c1de94f48abe271bbe1b465682/jiter-0.14.0-cp313-cp313-win32.whl", hash = "sha256:882bcb9b334318e233950b8be366fe5f92c86b66a7e449e76975dfd6d776a01f", size = 205699, upload-time = "2026-04-10T14:27:04.662Z" }, + { url = "https://files.pythonhosted.org/packages/4c/19/3f339a5a7f14a11730e67f6be34f9d5105751d547b615ef593fa122a5ded/jiter-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:9b8c571a5dba09b98bd3462b5a53f27209a5cbbe85670391692ede71974e979f", size = 201323, upload-time = "2026-04-10T14:27:06.139Z" }, + { url = "https://files.pythonhosted.org/packages/50/56/752dd89c84be0e022a8ea3720bcfa0a8431db79a962578544812ce061739/jiter-0.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:34f19dcc35cb1abe7c369b3756babf8c7f04595c0807a848df8f26ef8298ef92", size = 191099, upload-time = "2026-04-10T14:27:07.564Z" }, + { url = "https://files.pythonhosted.org/packages/91/28/292916f354f25a1fe8cf2c918d1415c699a4a659ae00be0430e1c5d9ffea/jiter-0.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e89bcd7d426a75bb4952c696b267075790d854a07aad4c9894551a82c5b574ab", size = 320880, upload-time = "2026-04-10T14:27:09.326Z" }, + { url = "https://files.pythonhosted.org/packages/ad/c7/b002a7d8b8957ac3d469bd59c18ef4b1595a5216ae0de639a287b9816023/jiter-0.14.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b25beaa0d4447ea8c7ae0c18c688905d34840d7d0b937f2f7bdd52162c98a40", size = 346563, upload-time = "2026-04-10T14:27:11.287Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3b/f8d07580d8706021d255a6356b8fab13ee4c869412995550ce6ed4ddf97d/jiter-0.14.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:651a8758dd413c51e3b7f6557cdc6921faf70b14106f45f969f091f5cda990ea", size = 357928, upload-time = "2026-04-10T14:27:12.729Z" }, + { url = "https://files.pythonhosted.org/packages/47/5b/ac1a974da29e35507230383110ffec59998b290a8732585d04e19a9eb5ba/jiter-0.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e1a7eead856a5038a8d291f1447176ab0b525c77a279a058121b5fccee257f6f", size = 203519, upload-time = "2026-04-10T14:27:14.125Z" }, + { url = "https://files.pythonhosted.org/packages/96/6d/9fc8433d667d2454271378a79747d8c76c10b51b482b454e6190e511f244/jiter-0.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e692633a12cda97e352fdcd1c4acc971b1c28707e1e33aeef782b0cbf051975", size = 190113, upload-time = "2026-04-10T14:27:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/4f/1e/354ed92461b165bd581f9ef5150971a572c873ec3b68a916d5aa91da3cc2/jiter-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:6f396837fc7577871ca8c12edaf239ed9ccef3bbe39904ae9b8b63ce0a48b140", size = 315277, upload-time = "2026-04-10T14:27:18.109Z" }, + { url = "https://files.pythonhosted.org/packages/a6/95/8c7c7028aa8636ac21b7a55faef3e34215e6ed0cbf5ae58258427f621aa3/jiter-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a4d50ea3d8ba4176f79754333bd35f1bbcd28e91adc13eb9b7ca91bc52a6cef9", size = 315923, upload-time = "2026-04-10T14:27:19.603Z" }, + { url = "https://files.pythonhosted.org/packages/47/40/e2a852a44c4a089f2681a16611b7ce113224a80fd8504c46d78491b47220/jiter-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce17f8a050447d1b4153bda4fb7d26e6a9e74eb4f4a41913f30934c5075bf615", size = 344943, upload-time = "2026-04-10T14:27:21.262Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1f/670f92adee1e9895eac41e8a4d623b6da68c4d46249d8b556b60b63f949e/jiter-0.14.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4f1c4b125e1652aefbc2e2c1617b60a160ab789d180e3d423c41439e5f32850", size = 369725, upload-time = "2026-04-10T14:27:22.766Z" }, + { url = "https://files.pythonhosted.org/packages/01/2f/541c9ba567d05de1c4874a0f8f8c5e3fd78e2b874266623da9a775cf46e0/jiter-0.14.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be808176a6a3a14321d18c603f2d40741858a7c4fc982f83232842689fe86dd9", size = 461210, upload-time = "2026-04-10T14:27:24.315Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a9/c31cbec09627e0d5de7aeaec7690dba03e090caa808fefd8133137cf45bc/jiter-0.14.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26679d58ba816f88c3849306dd58cb863a90a1cf352cdd4ef67e30ccf8a77994", size = 380002, upload-time = "2026-04-10T14:27:26.155Z" }, + { url = "https://files.pythonhosted.org/packages/50/02/3c05c1666c41904a2f607475a73e7a4763d1cbde2d18229c4f85b22dc253/jiter-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80381f5a19af8fa9aef743f080e34f6b25ebd89656475f8cf0470ec6157052aa", size = 354678, upload-time = "2026-04-10T14:27:27.701Z" }, + { url = "https://files.pythonhosted.org/packages/7d/97/e15b33545c2b13518f560d695f974b9891b311641bdcf178d63177e8801e/jiter-0.14.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:004df5fdb8ecbd6d99f3227df18ba1a259254c4359736a2e6f036c944e02d7c5", size = 358920, upload-time = "2026-04-10T14:27:29.256Z" }, + { url = "https://files.pythonhosted.org/packages/ad/d2/8b1461def6b96ba44530df20d07ef7a1c7da22f3f9bf1727e2d611077bf1/jiter-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cff5708f7ed0fa098f2b53446c6fa74c48469118e5cd7497b4f1cd569ab06928", size = 394512, upload-time = "2026-04-10T14:27:31.344Z" }, + { url = "https://files.pythonhosted.org/packages/e3/88/837566dd6ed6e452e8d3205355afd484ce44b2533edfa4ed73a298ea893e/jiter-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:2492e5f06c36a976d25c7cc347a60e26d5470178d44cde1b9b75e60b4e519f28", size = 521120, upload-time = "2026-04-10T14:27:33.299Z" }, + { url = "https://files.pythonhosted.org/packages/89/6b/b00b45c4d1b4c031777fe161d620b755b5b02cdade1e316dcb46e4471d63/jiter-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:7609cfbe3a03d37bfdbf5052012d5a879e72b83168a363deae7b3a26564d57de", size = 553668, upload-time = "2026-04-10T14:27:34.868Z" }, + { url = "https://files.pythonhosted.org/packages/ad/d8/6fe5b42011d19397433d345716eac16728ac241862a2aac9c91923c7509a/jiter-0.14.0-cp314-cp314-win32.whl", hash = "sha256:7282342d32e357543565286b6450378c3cd402eea333fc1ebe146f1fabb306fc", size = 207001, upload-time = "2026-04-10T14:27:36.455Z" }, + { url = "https://files.pythonhosted.org/packages/e5/43/5c2e08da1efad5e410f0eaaabeadd954812612c33fbbd8fd5328b489139d/jiter-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd77945f38866a448e73b0b7637366afa814d4617790ecd88a18ca74377e6c02", size = 202187, upload-time = "2026-04-10T14:27:38Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1f/6e39ac0b4cdfa23e606af5b245df5f9adaa76f35e0c5096790da430ca506/jiter-0.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:f2d4c61da0821ee42e0cdf5489da60a6d074306313a377c2b35af464955a3611", size = 192257, upload-time = "2026-04-10T14:27:39.504Z" }, + { url = "https://files.pythonhosted.org/packages/05/57/7dbc0ffbbb5176a27e3518716608aa464aee2e2887dc938f0b900a120449/jiter-0.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1bf7ff85517dd2f20a5750081d2b75083c1b269cf75afc7511bdf1f9548beb3b", size = 323441, upload-time = "2026-04-10T14:27:41.039Z" }, + { url = "https://files.pythonhosted.org/packages/83/6e/7b3314398d8983f06b557aa21b670511ec72d3b79a68ee5e4d9bff972286/jiter-0.14.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8ef8791c3e78d6c6b157c6d360fbb5c715bebb8113bc6a9303c5caff012754a", size = 348109, upload-time = "2026-04-10T14:27:42.552Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4f/8dc674bcd7db6dba566de73c08c763c337058baff1dbeb34567045b27cdc/jiter-0.14.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e74663b8b10da1fe0f4e4703fd7980d24ad17174b6bb35d8498d6e3ebce2ae6a", size = 368328, upload-time = "2026-04-10T14:27:44.574Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5f/188e09a1f20906f98bbdec44ed820e19f4e8eb8aff88b9d1a5a497587ff3/jiter-0.14.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1aca29ba52913f78362ec9c2da62f22cdc4c3083313403f90c15460979b84d9b", size = 463301, upload-time = "2026-04-10T14:27:46.717Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f0/19046ef965ed8f349e8554775bb12ff4352f443fbe12b95d31f575891256/jiter-0.14.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b39b7d87a952b79949af5fef44d2544e58c21a28da7f1bae3ef166455c61746", size = 378891, upload-time = "2026-04-10T14:27:48.32Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c3/da43bd8431ee175695777ee78cf0e93eacbb47393ff493f18c45231b427d/jiter-0.14.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d918a68b26e9fab068c2b5453577ef04943ab2807b9a6275df2a812599a310", size = 360749, upload-time = "2026-04-10T14:27:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/72/26/e054771be889707c6161dbdec9c23d33a9ec70945395d70f07cfea1e9a6f/jiter-0.14.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:b08997c35aee1201c1a5361466a8fb9162d03ae7bf6568df70b6c859f1e654a4", size = 358526, upload-time = "2026-04-10T14:27:51.504Z" }, + { url = "https://files.pythonhosted.org/packages/c3/0f/7bea65ea2a6d91f2bf989ff11a18136644392bf2b0497a1fa50934c30a9c/jiter-0.14.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:260bf7ca20704d58d41f669e5e9fe7fe2fa72901a6b324e79056f5d52e9c9be2", size = 393926, upload-time = "2026-04-10T14:27:53.368Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/b1ff7d70deef61ac0b7c6c2f12d2ace950cdeecb4fdc94500a0926802857/jiter-0.14.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:37826e3df29e60f30a382f9294348d0238ef127f4b5d7f5f8da78b5b9e050560", size = 521052, upload-time = "2026-04-10T14:27:55.058Z" }, + { url = "https://files.pythonhosted.org/packages/0b/7b/3b0649983cbaf15eda26a414b5b1982e910c67bd6f7b1b490f3cfc76896a/jiter-0.14.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:645be49c46f2900937ba0eaf871ad5183c96858c0af74b6becc7f4e367e36e06", size = 553716, upload-time = "2026-04-10T14:27:57.269Z" }, + { url = "https://files.pythonhosted.org/packages/97/f8/33d78c83bd93ae0c0af05293a6660f88a1977caef39a6d72a84afab94ce0/jiter-0.14.0-cp314-cp314t-win32.whl", hash = "sha256:2f7877ed45118de283786178eceaf877110abacd04fde31efff3940ae9672674", size = 207957, upload-time = "2026-04-10T14:27:59.285Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ac/2b760516c03e2227826d1f7025d89bf6bf6357a28fe75c2a2800873c50bf/jiter-0.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:14c0cb10337c49f5eafe8e7364daca5e29a020ea03580b8f8e6c597fed4e1588", size = 204690, upload-time = "2026-04-10T14:28:00.962Z" }, + { url = "https://files.pythonhosted.org/packages/dc/2e/a44c20c58aeed0355f2d326969a181696aeb551a25195f47563908a815be/jiter-0.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5419d4aa2024961da9fe12a9cfe7484996735dca99e8e090b5c88595ef1951ff", size = 191338, upload-time = "2026-04-10T14:28:02.853Z" }, + { url = "https://files.pythonhosted.org/packages/32/a1/ef34ca2cab2962598591636a1804b93645821201cc0095d4a93a9a329c9d/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a25ffa2dbbdf8721855612f6dca15c108224b12d0c4024d0ac3d7902132b4211", size = 311366, upload-time = "2026-04-10T14:28:27.943Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/520576a532a6b8a6f42747afed289c8448c879a34d7802fe2c832d4fd38f/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ac9cbaa86c10996b92bd12c91659b60f939f8e28fcfa6bc11a0e90a774ce95b", size = 309873, upload-time = "2026-04-10T14:28:29.688Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7c/c16db114ea1f2f532f198aa8dc39585026af45af362c69a0492f31bc4821/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:844e73b6c56b505e9e169234ea3bdea2ea43f769f847f47ac559ba1d2361ebea", size = 344816, upload-time = "2026-04-10T14:28:31.348Z" }, + { url = "https://files.pythonhosted.org/packages/99/8f/15e7741ff19e9bcd4d753f7ff22f988fd54592f134ca13701c13ea8c20e0/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52c076f187405fc21523c746c04399c9af8ece566077ed147b2126f2bcba577", size = 351445, upload-time = "2026-04-10T14:28:33.093Z" }, + { url = "https://files.pythonhosted.org/packages/21/42/9042c3f3019de4adcb8c16591c325ec7255beea9fcd33a42a43f3b0b1000/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:fbd9e482663ca9d005d051330e4d2d8150bb208a209409c10f7e7dfdf7c49da9", size = 308810, upload-time = "2026-04-10T14:28:34.673Z" }, + { url = "https://files.pythonhosted.org/packages/60/cf/a7e19b308bd86bb04776803b1f01a5f9a287a4c55205f4708827ee487fbf/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:33a20d838b91ef376b3a56896d5b04e725c7df5bc4864cc6569cf046a8d73b6d", size = 308443, upload-time = "2026-04-10T14:28:36.658Z" }, + { url = "https://files.pythonhosted.org/packages/ca/44/e26ede3f0caeff93f222559cb0cc4ca68579f07d009d7b6010c5b586f9b1/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:432c4db5255d86a259efde91e55cb4c8d18c0521d844c9e2e7efcce3899fb016", size = 343039, upload-time = "2026-04-10T14:28:38.356Z" }, + { url = "https://files.pythonhosted.org/packages/da/e9/1f9ada30cef7b05e74bb06f52127e7a724976c225f46adb65c37b1dadfb6/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f00d94b281174144d6532a04b66a12cb866cbdc47c3af3bfe2973677f9861a", size = 349613, upload-time = "2026-04-10T14:28:40.066Z" }, +] + +[[package]] +name = "joserfc" +version = "1.6.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/dc/5f768c2e391e9afabe5d18e3221346deb5fb6338565f1ccc9e7c6d7befdd/joserfc-1.6.5.tar.gz", hash = "sha256:1482a7db78fb4602e44ed89e51b599d052e091288c7c532c5b694e20149dec48", size = 231881, upload-time = "2026-05-06T04:58:13.408Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/3b/ad1cb22e75c963b1f07c8a2329bf47227ce7e4361df5eb2fb101b2ce33ef/joserfc-1.6.5-py3-none-any.whl", hash = "sha256:e9878a0f8243fe7b95e11fdda81374ca9f7a689e302751579d3dfdeec559675e", size = 70464, upload-time = "2026-05-06T04:58:11.668Z" }, +] + +[[package]] +name = "jsonref" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-path" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pathable" }, + { name = "pyyaml" }, + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/86/cfee6dd25843bec0760f456599a4f7e7e40221a934b9229fda0662c859bc/jsonschema_path-0.4.6.tar.gz", hash = "sha256:c89eb635f4d497c9ac328eeff359c489755838806a7d033510a692e9576f5c4b", size = 15302, upload-time = "2026-04-27T18:57:08.412Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/43/3d3065c05a04bb550c143bfbb8e4fd7022cd327e1082bf257bac74923783/jsonschema_path-0.4.6-py3-none-any.whl", hash = "sha256:451354b5311fa955c3144e6e4e255388c751c0121c5570ec5bb9291dd42d08c9", size = 19565, upload-time = "2026-04-27T18:57:06.792Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "keyring" +version = "25.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mcp" +version = "1.27.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/83/d1efe7c2980d8a3afa476f4e3d42d53dd54c0ab94c27bee5d755b45c8b73/mcp-1.27.1.tar.gz", hash = "sha256:0f47e1820f8f8f941466b39749eb1d1839a04caddca2bc60e9d46e8a99914924", size = 608458, upload-time = "2026-05-08T16:50:12.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/73/42d9596facebdb533b7f0b86c1b0364ef350d1f8ba78b1052e8a58b48b65/mcp-1.27.1-py3-none-any.whl", hash = "sha256:1af3c4203b329430fde7a87b4fcb6392a041f5cb851fd68fc674016ab4e7c06f", size = 216260, upload-time = "2026-05-08T16:50:10.547Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "more-itertools" +version = "11.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "multiprocess" +version = "0.70.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/b6/10832f96b499690854e574360be342a282f5f7dba58eff791299ff6c0637/multiprocess-0.70.19-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:02e5c35d7d6cd2bdc89c1858867f7bde4012837411023a4696c148c1bdd7c80e", size = 135131, upload-time = "2026-01-19T06:47:20.479Z" }, + { url = "https://files.pythonhosted.org/packages/99/50/faef2d8106534b0dc4a0b772668a1a99682696ebf17d3c0f13f2ed6a656a/multiprocess-0.70.19-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:79576c02d1207ec405b00cabf2c643c36070800cca433860e14539df7818b2aa", size = 135131, upload-time = "2026-01-19T06:47:21.879Z" }, + { url = "https://files.pythonhosted.org/packages/94/b1/0b71d18b76bf423c2e8ee00b31db37d17297ab3b4db44e188692afdca628/multiprocess-0.70.19-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c6b6d78d43a03b68014ca1f0b7937d965393a670c5de7c29026beb2258f2f896", size = 135134, upload-time = "2026-01-19T06:47:23.262Z" }, + { url = "https://files.pythonhosted.org/packages/7e/aa/714635c727dbfc251139226fa4eaf1b07f00dc12d9cd2eb25f931adaf873/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1bbf1b69af1cf64cd05f65337d9215b88079ec819cd0ea7bac4dab84e162efe7", size = 144743, upload-time = "2026-01-19T06:47:24.562Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e1/155f6abf5e6b5d9cef29b6d0167c180846157a4aca9b9bee1a217f67c959/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5be9ec7f0c1c49a4f4a6fd20d5dda4aeabc2d39a50f4ad53720f1cd02b3a7c2e", size = 144738, upload-time = "2026-01-19T06:47:26.636Z" }, + { url = "https://files.pythonhosted.org/packages/af/cb/f421c2869d75750a4f32301cc20c4b63fab6376e9a75c8e5e655bdeb3d9b/multiprocess-0.70.19-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1c3dce098845a0db43b32a0b76a228ca059a668071cfeaa0f40c36c0b1585d45", size = 144741, upload-time = "2026-01-19T06:47:27.985Z" }, + { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" }, + { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414, upload-time = "2026-01-19T06:47:35.915Z" }, + { url = "https://files.pythonhosted.org/packages/a0/61/af9115673a5870fd885247e2f1b68c4f1197737da315b520a91c757a861a/multiprocess-0.70.19-py314-none-any.whl", hash = "sha256:e8cc7fbdff15c0613f0a1f1f8744bef961b0a164c0ca29bdff53e9d2d93c5e5f", size = 160318, upload-time = "2026-01-19T06:47:37.497Z" }, + { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/50/8e/b8041bc719f056afd864478029d52214789341ac6583437b0ee5031e9530/numpy-2.4.5.tar.gz", hash = "sha256:ca670567a5683b7c1670ec03e0ddd5862e10934e92a70751d68d7b7b74ca7f9f", size = 20735669, upload-time = "2026-05-15T20:25:19.492Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/44/1383ee4d1e916a9e610e46c876b5c83ea023526117d23cd911983929ec34/numpy-2.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3176dc8ff71dbb593606f91a69ad0c3cd3303c7eb546af477370ab9edf760288", size = 16969261, upload-time = "2026-05-15T20:22:23.036Z" }, + { url = "https://files.pythonhosted.org/packages/3d/61/54bacfbec7550bc398e6b6d9a861db35d64f75844e1d7920f5722c3cd5e7/numpy-2.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1811150e5148f5a01a7cc282cb2f489b4a3050a773e173adb480e507bad3a3d7", size = 14964009, upload-time = "2026-05-15T20:22:25.819Z" }, + { url = "https://files.pythonhosted.org/packages/7a/55/fe86c64561761f185339c26001164a2687bd4787af681e961431abd2d534/numpy-2.4.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0d63a780070871210853ba01e90b88f9b85cf2abf63a7f143d5127189265ddf6", size = 5469106, upload-time = "2026-05-15T20:22:28.13Z" }, + { url = "https://files.pythonhosted.org/packages/2f/74/cf29b8317627f0e3aa2c9fb332d386bd734308cecd9e07da9f407d9ce0c3/numpy-2.4.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:0c6919cefafb3b76cd46a89dbb203bf1dd95529d2a6d09fef2d325d95d6a79d8", size = 6798945, upload-time = "2026-05-15T20:22:30.061Z" }, + { url = "https://files.pythonhosted.org/packages/80/a9/b61730a17fa87d5abb13ce560a1b4ce3485d37a13e03eb7b414e598e72f8/numpy-2.4.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d51efede1e58e8b11877536a5518f60e318d8ff69b89ad7b38ee5e431b24d772", size = 15967025, upload-time = "2026-05-15T20:22:32.328Z" }, + { url = "https://files.pythonhosted.org/packages/03/39/70bcd187eb4d223c21fde02c2bdfbffbffef3288cbb3947c04c74ae39a08/numpy-2.4.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07ce7e74da92d7c71b5df157b9758bcdd53d7fea10602154de3afd2b3ddc34dd", size = 16918685, upload-time = "2026-05-15T20:22:34.759Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/400fd1315bbe228af3937cf8a74e32023df6217af36077919d00adc382e4/numpy-2.4.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d7828234a13185effb34979e146f9921f2a65dfbbe215e6dbb57d6478fc8e059", size = 17322963, upload-time = "2026-05-15T20:22:37.557Z" }, + { url = "https://files.pythonhosted.org/packages/18/6a/bbbafb657e6f6ee826b4ecdb8722a2e0aae4a981888eaf59eae6a535cc13/numpy-2.4.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f96083adc3dfc1bbf778f2c79654d88115fa07074c97cb724fe9508f12d91c55", size = 18651594, upload-time = "2026-05-15T20:22:40.449Z" }, + { url = "https://files.pythonhosted.org/packages/de/0c/857a515154a2a18b0dfae04089600d166d352d473ec17a0680d879582d06/numpy-2.4.5-cp311-cp311-win32.whl", hash = "sha256:4ed78c904a638b6e5d7cd4db90c06fca5fc6ec2f28d258305368f454a50e79cf", size = 6233849, upload-time = "2026-05-15T20:22:43.139Z" }, + { url = "https://files.pythonhosted.org/packages/f0/66/d215f3fb93541617adb5d58b3b9508e8a6413e499711e0adc0b80bcb445d/numpy-2.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:079b0fad6f2899b23c5da89792b5409d2d83fc83e8bd5c2299cc9c397a264864", size = 12608238, upload-time = "2026-05-15T20:22:45.229Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c4/611d66d3fcfa931954d37a19ce5575f3283d023e89ff0df6ad43b334ae9c/numpy-2.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:d6c78e260b53affe9b395a9d54fc61f101f9521c4d9452c7e9e3718b19e2215b", size = 10479452, upload-time = "2026-05-15T20:22:47.962Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/3275231e98620002681c922e792db04d72c356e9d8073c387344fc0e4ff1/numpy-2.4.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:654fb8674b61b1c4bd568f944d13a908566fdcb0d797303521d4149d16da05ef", size = 16689166, upload-time = "2026-05-15T20:22:50.761Z" }, + { url = "https://files.pythonhosted.org/packages/db/23/000aab6a16bdec53307f0f72546b57a3ac9266a62d8c257bee97d85fd078/numpy-2.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4cd9f6fa7ce10dc4627f2bb81dd9075dab67e94632e04c2b638e12575ddaa862", size = 14699514, upload-time = "2026-05-15T20:22:53.678Z" }, + { url = "https://files.pythonhosted.org/packages/47/cc/ddaf3af9c46966fef5be879256f213d85a0c56c75d07a3b7defec7cf6b4c/numpy-2.4.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:4f5bc96d35d94e4ceab8b38a92241b4611e95dc44e63b9f1fa2a331858ee3507", size = 5204601, upload-time = "2026-05-15T20:22:56.257Z" }, + { url = "https://files.pythonhosted.org/packages/07/ea/627fadd11959b3c7759008f34c92a35af8ff942dd8284a66ced648bbe516/numpy-2.4.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4bb33e900ee81730ad77a258965134aa8ceac805124f7e5229347beda4b8d0aa", size = 6551360, upload-time = "2026-05-15T20:22:58.334Z" }, + { url = "https://files.pythonhosted.org/packages/a1/47/0728b986b8682d742ff68c16baa5af9d185484abfc635c5cc700f44e62be/numpy-2.4.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32f8f852273ef32b291201ac2a2c97629c4a1ee8632bb670e3443eaa09fc2e72", size = 15671157, upload-time = "2026-05-15T20:23:01.081Z" }, + { url = "https://files.pythonhosted.org/packages/d1/0b/b905ae82d9419dc38123523862db64978ca2954b69609c3ae8fdaca1084c/numpy-2.4.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:685681e956fc8dcb75adc6ff26694e1dfd738b24bd8d4696c51ca0110157f912", size = 16645703, upload-time = "2026-05-15T20:23:04.358Z" }, + { url = "https://files.pythonhosted.org/packages/5f/24/e27fc3f5236b4118ed9eed67111675f5c61a07ea333acec87c869c3b359d/numpy-2.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f64dd84b277a737eb59513f6b9bb6195bf41ab11941ef15b2562dbab43fa8ef", size = 17021018, upload-time = "2026-05-15T20:23:07.021Z" }, + { url = "https://files.pythonhosted.org/packages/d3/a7/9041af38d527ab80a06a93570a77e29425b41507ad41f6acf5da78cfb4a4/numpy-2.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b42d9496f79e3a728192f05a42d86e36163217b7cdecb3813d0028a0aa6b72d7", size = 18368768, upload-time = "2026-05-15T20:23:09.44Z" }, + { url = "https://files.pythonhosted.org/packages/49/82/326a014442f32c2663434fd424d9298791f47f8a0f17585ad60519a5606e/numpy-2.4.5-cp312-cp312-win32.whl", hash = "sha256:86d980970f5110595ca14855768073b08585fc1acc36895de303e039e7dee4a5", size = 5962819, upload-time = "2026-05-15T20:23:11.631Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/cbf5d391b0b3a5e8cad264603e2fae256b0bde8ce43566b13b78faedc659/numpy-2.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:3333dba6a4e611d666f69e177ba8fe4140366ff681a5feb2374d3fd4fff3acb6", size = 12321621, upload-time = "2026-05-15T20:23:14.305Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d0/0f18909d9bc37a5f3f969fc737d2bb5df9f2ff295f71b467e6f52a0d6c4e/numpy-2.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:4593d197270b894efeb538dcbe227e4bcf1c77f88c4c6bf933ead812cfaa4453", size = 10221430, upload-time = "2026-05-15T20:23:16.887Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a4/fb50657c7cab297bf34edcd60a074cb0647f61771430d6363575274160fe/numpy-2.4.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ef248460b645c102026b82337cc4e88231909c66dd77b59ec6d6cac7e44f277", size = 16684760, upload-time = "2026-05-15T20:23:19.436Z" }, + { url = "https://files.pythonhosted.org/packages/3e/43/87e731299b9408eda705b3b9cb31c7bceb9347d2af9cbb16b2b1e4b5bc0f/numpy-2.4.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4603622bdcdbf8dccb1d9d5b21d16a7aa4e473ae6c8e14048d846fd4ca2907a0", size = 14694117, upload-time = "2026-05-15T20:23:21.832Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/0b2bb8acea222e9dd6e582afc2bc553b89b8833cbdccc68e68f050fb31f8/numpy-2.4.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6c18d49c67689c562854b53fdc433b93e47c12952aa6fa6d59f185e1a5992419", size = 5199141, upload-time = "2026-05-15T20:23:24.066Z" }, + { url = "https://files.pythonhosted.org/packages/39/60/b6972b5d47033d90000f0097c81a98b9486589a2d7003bf725bff275cb0d/numpy-2.4.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:b1c663ddc641f4192e90511bec61a09bc231e3bbdb996cdc6edbcaa0e528d685", size = 6546954, upload-time = "2026-05-15T20:23:26.099Z" }, + { url = "https://files.pythonhosted.org/packages/c1/e9/ed667cb12c11ca0adde431f685d3a5dd78e6f78b27228c581c8415198e9e/numpy-2.4.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93793222b524f692f12b2f8752ce8b1d9d9125b2bfd5dbf0fb69c92c5e1ce86c", size = 15669430, upload-time = "2026-05-15T20:23:28.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/e5/679f6ffeb01294b0008e5ada4a113cb47617bc0e1819a529fd7973c6d7f4/numpy-2.4.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1616bde34b2bcba2fa9bde06217ce00da4f3d1bdfb264d54525a99e8fe170d83", size = 16633390, upload-time = "2026-05-15T20:23:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/36/46/42bfffc9a780ec902ccd7470d3219192ee82b7b442710307dd85b4d121b0/numpy-2.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:09d7d97da1c2c62f4818b3e150a57572ff8dcf1cf5ac501aac832ffd4ebd9566", size = 17020709, upload-time = "2026-05-15T20:23:34.08Z" }, + { url = "https://files.pythonhosted.org/packages/44/00/3e840bfee0cc6cec22209f2c97057f26eeb30de031e4933b4dfc0395416c/numpy-2.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d68d0b355ab2e39fe0de59001d7151dfdbbb880ef67baeed806661e03df5097", size = 18357818, upload-time = "2026-05-15T20:23:36.965Z" }, + { url = "https://files.pythonhosted.org/packages/72/cb/3447b400b9da84134575486f0f656541559b00d4b262477bce9b678bbca8/numpy-2.4.5-cp313-cp313-win32.whl", hash = "sha256:fe28b64777ddfa0eca9b5f51474034ebe3dcb8324f48f27b28f479085673ae33", size = 5961114, upload-time = "2026-05-15T20:23:39.586Z" }, + { url = "https://files.pythonhosted.org/packages/28/f9/a90d2220ffcdc0798f5d55bb5d5463cd6254ec9ef43f384dae80217d7a2f/numpy-2.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:fb4a6c9c537d6ccec9cc4aeae4261bd3cc79b070c67ddc0646f5b1c07fddde42", size = 12318553, upload-time = "2026-05-15T20:23:41.436Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c9/96f531fb3234545315152d34efdf3de7daee81254448447eb619e8d16967/numpy-2.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:6d7df2da2e7ea0624a43aa368104b3a3ce14aae98ad4bb2c9a93fecef76f1c97", size = 10222200, upload-time = "2026-05-15T20:23:43.681Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f4/a291caab5a3c520babf93ff77c54fd5fdb1ebbc3296cee2eb2146ce773b1/numpy-2.4.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2a235607a18df941760a695927051af4b1cd5d3ee85840d0e2af816785771feb", size = 14821438, upload-time = "2026-05-15T20:23:45.911Z" }, + { url = "https://files.pythonhosted.org/packages/85/26/13dbb1159b864370568e7309063fd72667984df89db74e9caeb175d067c7/numpy-2.4.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:58dcf64969d870f36bc7fbd557d2617e997db7dc06261b6e3327148ea460d0a4", size = 5326663, upload-time = "2026-05-15T20:23:48.18Z" }, + { url = "https://files.pythonhosted.org/packages/7c/99/d233408072a0e019e2288e27edd23f7d572ccd4a73d1539baa3270ede85d/numpy-2.4.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:235f54b0156274d8fa3155db3ed6d2f401c7e8f3367c90db0a12f02a58fde6ed", size = 6646874, upload-time = "2026-05-15T20:23:49.856Z" }, + { url = "https://files.pythonhosted.org/packages/c5/00/eeb6f193dfe767725e952e0464f3e51f44145c5dd261cd7389aa36ac0713/numpy-2.4.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3b5bb65437a3555c648e706475db01c645559ca80dc8b03e4f202ea757e0d6", size = 15728147, upload-time = "2026-05-15T20:23:51.655Z" }, + { url = "https://files.pythonhosted.org/packages/e5/c9/b8ed039f1fde1b13a8807c893e7e2f9432a379f4d6401edecf0028da5b2c/numpy-2.4.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7f09a7e5f017d7098c66522097c96257411c9620c0926212200d66bc8cee3976", size = 16681770, upload-time = "2026-05-15T20:23:53.933Z" }, + { url = "https://files.pythonhosted.org/packages/11/5b/0198ef6cb7016eca6d895d392106012138127fab23f46637e76d5e25c9f5/numpy-2.4.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:993a88d8fdd8554466a8765cd8bacd97ba56b70ca6b0a04bcdca77f5afed4222", size = 17086218, upload-time = "2026-05-15T20:23:56.646Z" }, + { url = "https://files.pythonhosted.org/packages/f0/fe/8821f3cfc660ae84c92ee158505941874b62c56a42e035a41425228cd8cf/numpy-2.4.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:84f58bed609b5669f5ad3d597901a4f1f86ee5b3c3708aaa55f05b4fe6e0f656", size = 18403542, upload-time = "2026-05-15T20:23:59.173Z" }, + { url = "https://files.pythonhosted.org/packages/0e/00/e64ecaf498865e7b091f57658b2c522503e5d1b70e43b807f5f8247e1d88/numpy-2.4.5-cp313-cp313t-win32.whl", hash = "sha256:7200c58f3f933ca61e66346667dcc8510bb111995e9ce15398a731e6a4afa4bb", size = 6084903, upload-time = "2026-05-15T20:24:01.506Z" }, + { url = "https://files.pythonhosted.org/packages/20/c0/354997dedaf74e8311c2cf9a6027b476fd8d424cb92189cc0ae2b25f501c/numpy-2.4.5-cp313-cp313t-win_amd64.whl", hash = "sha256:c26c71080d35db5002102f5d9ff614d45de02aa1f7802943e691e063e5ee93bc", size = 12458420, upload-time = "2026-05-15T20:24:03.735Z" }, + { url = "https://files.pythonhosted.org/packages/66/dc/917ee5ea4a31ca1a6e4c9a85386477efa318dcc60db257c5ef4adda096c1/numpy-2.4.5-cp313-cp313t-win_arm64.whl", hash = "sha256:2caa576d1707b275cba1aeb60a5c50daa6fa2a3f28ecb08123bc05fd439005db", size = 10291826, upload-time = "2026-05-15T20:24:06.535Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c1/3be0bf102fc17cff5bd142e3be0bfffabec6fa46da0a462396c76b0765d0/numpy-2.4.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:889ca2c072315de638a5194a772aa1fa2df92bdd6175f6a222d4784040424b61", size = 16683455, upload-time = "2026-05-15T20:24:08.988Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3e/0742d724901fa36bc54b338c6e62e463a7601180da896aa44978f0adf004/numpy-2.4.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:89e89304fb1f8c3f0ecfa4a7d48f311dd79771336a940e920159d643d1307e77", size = 14704577, upload-time = "2026-05-15T20:24:11.542Z" }, + { url = "https://files.pythonhosted.org/packages/25/1c/196c610ff4c6782d697ba780ebdc1616be143213701bf22c1a270f3bf7dd/numpy-2.4.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:144fcc5a3a17679b2b82543b4a2d8dd29937230a7af13232b5f753872feb6361", size = 5209756, upload-time = "2026-05-15T20:24:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/52/c0/23fb1bc506f774e03db66219a2830e720f4d3dbcaaddf855a7ff7bb6d96f/numpy-2.4.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:398bb16772b265b9fa5c07b07072646ea97137c10ffb62a9a087b277fc825c29", size = 6543937, upload-time = "2026-05-15T20:24:16.223Z" }, + { url = "https://files.pythonhosted.org/packages/9f/49/db4662c26e68520afcc84d672a6f9f5294063dee0e57a46d61afdaa7f9ed/numpy-2.4.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb352e7b8876da1249e72254736d6c58c505fa4e58a3d7e30efca241ca9ca9ce", size = 15685292, upload-time = "2026-05-15T20:24:17.978Z" }, + { url = "https://files.pythonhosted.org/packages/43/80/1315439acedd8398319bac177d6de3d48ab39c62cc0c810f74f0a9a73996/numpy-2.4.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7341b08ff8124d7353939778e2707b8732d03c78c1c30e0815aba2dacbe1245a", size = 16638528, upload-time = "2026-05-15T20:24:20.478Z" }, + { url = "https://files.pythonhosted.org/packages/56/81/364388600932618fe735d97fdd2437cb8dd87a23377ac11d8b9d5db098b7/numpy-2.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:deb01226f012539f3945261ffe1c10aec081a0fa0a5c925419933c70f3ae2d23", size = 17036709, upload-time = "2026-05-15T20:24:22.949Z" }, + { url = "https://files.pythonhosted.org/packages/32/4a/a1185b18a94a6d9587e54b437e7d0ba36ecf6e614f1bea03f5249912c64e/numpy-2.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d888bdf7335f76878c3c7b264ac1ff089863e211ec81249f9fb5795c2183dc25", size = 18363254, upload-time = "2026-05-15T20:24:25.402Z" }, + { url = "https://files.pythonhosted.org/packages/b9/8e/95c1d2ed15ae97750ede8c8a0ac487c9c01207afff430f47078b1d9d7dc5/numpy-2.4.5-cp314-cp314-win32.whl", hash = "sha256:15f90d1256e9b2320aff24fde44815b787ab6d7c49a1a11bfd8138b321c5f080", size = 6010184, upload-time = "2026-05-15T20:24:27.852Z" }, + { url = "https://files.pythonhosted.org/packages/aa/92/d063df4d63d988b20d881856c74df76c0c1786229bb870f3a52af0981d4d/numpy-2.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4bd2cd4ef9c0afa87de73723c0a33c0edff62143e1432917458e26d3d195d87f", size = 12450344, upload-time = "2026-05-15T20:24:29.856Z" }, + { url = "https://files.pythonhosted.org/packages/3d/64/c0ae481f7c3b2f85869bcd8fc5d30aa7c96b394162eef9c9315957f115c5/numpy-2.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:db304568c650e9d7039744d3575d0d287754debb2057d7c7b8cdfdc2c487a957", size = 10495674, upload-time = "2026-05-15T20:24:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/57/89/c5a4c677acf17aa50ba09a15e61812f90baac42bb6ca38d112e005858351/numpy-2.4.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6de2883e0d2c63eae1bab1a84b390dca74aabb3d20ea1f5d58f360853c83abf3", size = 14824078, upload-time = "2026-05-15T20:24:34.669Z" }, + { url = "https://files.pythonhosted.org/packages/e7/52/57e7144284f6b51ba93523e495ff239260b1ecd5257e3700a436332e5688/numpy-2.4.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:06760fe73ae5005008748d182de612c733542af3cde063d532cd2127561b27be", size = 5329246, upload-time = "2026-05-15T20:24:36.957Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b3/09dbce80fd4a7db4318f2fc01eec0ae76f29306442b5a32d4b811d082cdf/numpy-2.4.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:4b51a01745cb04cc19278482207444b4d30728ce91c28d27a3bfae5fc6ff24c7", size = 6649877, upload-time = "2026-05-15T20:24:38.861Z" }, + { url = "https://files.pythonhosted.org/packages/30/c2/dbdb23e82d540b757690ef13f011c386fca6a63848eec6136baf8ce7cbed/numpy-2.4.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a05636d7937d0936f271e5ba957fa8d746b5be3c2025caa1a2508f4fe521d40", size = 15730534, upload-time = "2026-05-15T20:24:41.168Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bd/68f6e9b3c20decf40ac06708a7b506757e3a8588efed32988d1b747316be/numpy-2.4.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b86f56048ed09c3bbe48962a7dff077c2fd3274f8cf981800f3b38eac49cc3", size = 16679741, upload-time = "2026-05-15T20:24:44.874Z" }, + { url = "https://files.pythonhosted.org/packages/39/1d/0fcac0b6b4ea1b50ca8fca05a34bed5c8d56e34c1cb5ffb04cf76109ac3c/numpy-2.4.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:130d58151c4db23e9fa860b84784e219a3aa3e030acc88a493ea37006c4dfd4c", size = 17085598, upload-time = "2026-05-15T20:24:47.603Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e8/a472b2564cf6cc498ad7aa9741d9832648221b8ab8cc0dbef41faa248ede/numpy-2.4.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d475afc8cbe935ff5944f753d863bba774d7f4e1feaaa4102901e3e053ca5963", size = 18403855, upload-time = "2026-05-15T20:24:50.474Z" }, + { url = "https://files.pythonhosted.org/packages/b9/a4/da82196f8cc4bd28ecf17bd57008c84f3d4696caf06753d9bad45e4ad749/numpy-2.4.5-cp314-cp314t-win32.whl", hash = "sha256:27f4a6dc26353a860b348961b9aa9e009835688b435cfa105e873b8dc2c726f5", size = 6156900, upload-time = "2026-05-15T20:24:53.134Z" }, + { url = "https://files.pythonhosted.org/packages/98/31/860959b91a73d9a085006554fa3850da51a7ffab64599bac5097243438ab/numpy-2.4.5-cp314-cp314t-win_amd64.whl", hash = "sha256:76ac6e90f5e226011c88f9b7040a4bcae612518bc7e9adc127e697a13b28ad1a", size = 12638906, upload-time = "2026-05-15T20:24:55.009Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2a/bbd3097913083ad07c0f28fc9629666221fc18923e17ce97ae22a5dccdd6/numpy-2.4.5-cp314-cp314t-win_arm64.whl", hash = "sha256:7c392e2c1bf596701d3c6832be7567eab5d5b0a13865036c33365ee097d37f8b", size = 10565875, upload-time = "2026-05-15T20:24:57.425Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5d/9a644cfb841bc76b584afc3af1708b3bf6c5cb51fc84a7008246cd93b7b7/numpy-2.4.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6bf0bfc1c2e1db972e30b6cd3d4861f477f3af908b27799b239dc3cbe3eb4b95", size = 16847544, upload-time = "2026-05-15T20:24:59.746Z" }, + { url = "https://files.pythonhosted.org/packages/56/8f/4fe5e3ba76d858dae1fe79078818c0520447335be0082c0dedf82719cc08/numpy-2.4.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:73d664413fb97229149c4711ef56531a6fe8c15c1c2626b0bbe497b84c287e70", size = 14889039, upload-time = "2026-05-15T20:25:03.179Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6f/79f195abf922ecc43e7d0eb6cc969462a71b524a35bcd1fa26b4a1d7406a/numpy-2.4.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:b35bee5ef99e8d227a07829bee2e864fcb65f7c157646fcd8ec8b4b45dd8b88f", size = 5394106, upload-time = "2026-05-15T20:25:05.659Z" }, + { url = "https://files.pythonhosted.org/packages/58/6f/79cd6247205802bcbd10b40ea087e20ded526e10e9be224d34de832b216e/numpy-2.4.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:02981d0fc9f9ce147643d552966d47f329a02f7ecb3b113e84207242f20dfa83", size = 6708718, upload-time = "2026-05-15T20:25:08.071Z" }, + { url = "https://files.pythonhosted.org/packages/d7/22/5f378a9d4633c98f28c4709d4144b1a4630c5c09e109d2e781e2d26c8fe1/numpy-2.4.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e63caf31a1df06338ae63d999f7a33a675ced62eea9c9b02db4b1c1f45cff38", size = 15798292, upload-time = "2026-05-15T20:25:10.689Z" }, + { url = "https://files.pythonhosted.org/packages/63/1c/cec582febef798c99888892d92dc1d28dfe29cb427c41f44d13d0dec208f/numpy-2.4.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8fc52b85a7b45e474be53eddf08e006d22e381a4e41bcde8e4aa08da0e7d198", size = 16747406, upload-time = "2026-05-15T20:25:13.879Z" }, + { url = "https://files.pythonhosted.org/packages/b1/dc/d358a16a6fec86cf736b8fbe67386044b3fa2aded1a80cff90e836799301/numpy-2.4.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:40c71d50a4da1a7c317af419461052d3911a5770bfc5fd55baf52cc45e7a2c20", size = 12504085, upload-time = "2026-05-15T20:25:16.667Z" }, +] + +[[package]] +name = "nvidia-cublas" +version = "13.1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cuda-nvrtc" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.20.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas" }, + { name = "nvidia-cusparse" }, + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" }, + { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.29.7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" }, + { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, +] + +[[package]] +name = "openai" +version = "2.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/50/5901f01ef14e6c27788beb91e54fef5d6204fb5fb9e97402fc8a14de2e32/openai-2.37.0.tar.gz", hash = "sha256:f4bc562cc5f3a43d40d678105572d9d44765f6e0f50c125f63055419b72f4bd9", size = 754706, upload-time = "2026-05-15T22:30:35.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/4c/bce61680d0699a78a405fd9a67989b175ba020590428831aab2ab1d2be7c/openai-2.37.0-py3-none-any.whl", hash = "sha256:814633888b8f3b1ffd6615697c6e4ef93632d08b7c2e28c8c5ef3556e5a10107", size = 1303238, upload-time = "2026-05-15T22:30:32.767Z" }, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, +] + +[[package]] +name = "openenv-core" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "gradio" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, + { name = "tomli" }, + { name = "tomli-w" }, + { name = "typer" }, + { name = "uvicorn" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/d6/3bebe8afb55fcc3ea9251c4c2dfbab2879e31089bc91a8fe9696e5ce019b/openenv_core-0.3.0.tar.gz", hash = "sha256:c7fee2035badab5be497eb6f4afb2cb417de000f82cc19afd72fb5ec332c431d", size = 164720, upload-time = "2026-05-11T11:37:57.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/f5/aafa43138589bfd5d369a8d02ea365aae9d6fe55ac0b3894368d6d69bd03/openenv_core-0.3.0-py3-none-any.whl", hash = "sha256:859e875c9d5211b157c30fb9abc681606fcf0bf1b6ffcdf404678992823a1df0", size = 194313, upload-time = "2026-05-11T11:37:55.537Z" }, +] + +[package.optional-dependencies] +core = [ + { name = "fastapi" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "uvicorn" }, + { name = "websockets" }, +] + +[[package]] +name = "openenv-mini-swe-env" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "aiohttp" }, + { name = "datasets" }, + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "openenv-core", extra = ["core"] }, + { name = "pydantic" }, + { name = "requests" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, +] +train = [ + { name = "accelerate" }, + { name = "hf-sandbox" }, + { name = "torch" }, + { name = "trackio" }, + { name = "transformers" }, + { name = "trl" }, +] + +[package.metadata] +requires-dist = [ + { name = "accelerate", marker = "extra == 'train'", specifier = ">=1.13.0" }, + { name = "aiohttp", specifier = ">=3.13.5" }, + { name = "datasets", specifier = ">=4.8.0" }, + { name = "fastapi", specifier = ">=0.104.0" }, + { name = "fastmcp", specifier = ">=3.3.0" }, + { name = "hf-sandbox", marker = "extra == 'train'", specifier = ">=0.1.1" }, + { name = "openenv-core", extras = ["core"], specifier = ">=0.3.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, + { name = "requests", specifier = ">=2.25.0" }, + { name = "torch", marker = "extra == 'train'", specifier = ">=2.6.0" }, + { name = "trackio", marker = "extra == 'train'", specifier = ">=0.25.0" }, + { name = "transformers", marker = "extra == 'train'", specifier = ">=5.8.0" }, + { name = "trl", marker = "extra == 'train'", specifier = ">=1.4.0" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0" }, +] +provides-extras = ["train", "dev"] + +[[package]] +name = "opentelemetry-api" +version = "1.41.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/fc/b7564cbef36601aef0d6c9bc01f7badb64be8e862c2e1c3c5c3b43b53e4f/opentelemetry_api-1.41.1.tar.gz", hash = "sha256:0ad1814d73b875f84494387dae86ce0b12c68556331ce6ce8fe789197c949621", size = 71416, upload-time = "2026-04-24T13:15:38.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, +] + +[[package]] +name = "orjson" +version = "3.11.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/5d/b95ca542a001135cc250a49370f282f578c8f4e46cc8617d73775297eea8/orjson-3.11.9-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:135869ef917b8704ea0a94e01620e0c05021c15c52036e4663baffe75e72f8ce", size = 228986, upload-time = "2026-05-06T15:09:14.765Z" }, + { url = "https://files.pythonhosted.org/packages/80/01/be33fbff646e22f93398429ea645f20d2097aea1a6cdc1e6628e70125f83/orjson-3.11.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115ab5f5f4a0f203cc2a5f0fb09aee503a3f771aa08392949ab5ca230c4fbdbd", size = 132558, upload-time = "2026-05-06T15:09:17.431Z" }, + { url = "https://files.pythonhosted.org/packages/4e/61/73d49333bba660a075daccca10970dc6409ce1cf42ae4046646a19468aad/orjson-3.11.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4da3c38a2083ca4aaf9c2a36776cce3e9328e6647b10d118948f3cfb4913ffe4", size = 128213, upload-time = "2026-05-06T15:09:18.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/7d/30e844b3dac3f74aed66b1f984daf9db3c98c0328c03d965a9e8dc06449e/orjson-3.11.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53b50b0e14084b8f7e29c5ce84c5af0f1160169b30d8a6914231d97d2fe297d4", size = 135430, upload-time = "2026-05-06T15:09:20.257Z" }, + { url = "https://files.pythonhosted.org/packages/16/64/bd815f5c610b3facc204f26ba94e87a9eb49b0d83de3d5fc1eee2402d91b/orjson-3.11.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:231742b4a11dad8d5380a435962c57e91b7c37b79be858f4ef1c0df1a259897e", size = 146178, upload-time = "2026-05-06T15:09:21.616Z" }, + { url = "https://files.pythonhosted.org/packages/c7/35/e744fd36c79b339d27beb06068b5a08a8882ef5418804d0ce545a31f718d/orjson-3.11.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34fd2317602587321faab75ab76c623a0117e80841a6413654f04e47f339a8fb", size = 133068, upload-time = "2026-05-06T15:09:23.228Z" }, + { url = "https://files.pythonhosted.org/packages/2a/56/d54152b67b63a0b3e556cfc549d6ce84f74d7f425ddeadc6c8a74d913da7/orjson-3.11.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f3db16e69b667b132e0f305a833d5497da302d801508cbb051ed9a9819da47", size = 134217, upload-time = "2026-05-06T15:09:24.847Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ee/66154baf69f71c7164a268a5e888908aec5a0819d13c81d5e2755a257758/orjson-3.11.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0b34789fa0da61cf7bef0546b09c738fb195331e017e477096d129e9105ab03d", size = 141917, upload-time = "2026-05-06T15:09:26.647Z" }, + { url = "https://files.pythonhosted.org/packages/09/d3/c5824260ca8b9d7ba82648d042a3f8f4815d18c15bb98a1f30edd1bb2d83/orjson-3.11.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:87e4d4ab280b0c87424d47695bec2182caf8cfc17879ea78dab76680194abc13", size = 415356, upload-time = "2026-05-06T15:09:28.252Z" }, + { url = "https://files.pythonhosted.org/packages/64/cb/509c2e816fe4df641d93dc92f6a89adc8df3ada8ebdee2bd44aba3264c3c/orjson-3.11.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ace6c58523302d3b97b6ac5c38a5298a54b473762b6be82726b4265c41029f92", size = 148112, upload-time = "2026-05-06T15:09:29.783Z" }, + { url = "https://files.pythonhosted.org/packages/db/b5/3ceae56d2e4962979eedb023ba6a46a4bb65f333960379be0ca470686220/orjson-3.11.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:97d0d932803c1b164fde11cb542a9efcb1e0f63b184537cca65887147906ff48", size = 137112, upload-time = "2026-05-06T15:09:31.432Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7a/81fa3f2c7bef79b04cf2ab7838e5ac74b1f12511ceab979759b0275d6bb4/orjson-3.11.9-cp310-cp310-win32.whl", hash = "sha256:b3afcf569c15577a9fe64627292daa3e6b3a70f4fb77a5df246a87ec21681b94", size = 131706, upload-time = "2026-05-06T15:09:32.707Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d8/b64600f9083c7f151ad39717a5877fccbeb0ef6d7efcb55f971ce00b6bee/orjson-3.11.9-cp310-cp310-win_amd64.whl", hash = "sha256:8697ab6a080a5c46edaad50e2bc5bd8c7ca5c66442d24104fa44ec74910a8244", size = 127282, upload-time = "2026-05-06T15:09:33.955Z" }, + { url = "https://files.pythonhosted.org/packages/1e/51/3fb9e65ae76ee97bd611869a503fa3fc0a6e81dd8b737cf3003f682df7ff/orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f", size = 228522, upload-time = "2026-05-06T15:09:35.362Z" }, + { url = "https://files.pythonhosted.org/packages/16/fa/9d54b07cb3f3b0bfd57841478e42d7a0ece4a9f49f9907eecf5a45461687/orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c", size = 128463, upload-time = "2026-05-06T15:09:37.063Z" }, + { url = "https://files.pythonhosted.org/packages/88/b1/6ceafc2eefd0a553e3be77ce6c49d107e772485d9568629376171c50e634/orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5", size = 132306, upload-time = "2026-05-06T15:09:38.299Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/f11311285324a40aab1e3031385c50b635a7cd0734fdaf60c7e89a696f60/orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c", size = 127988, upload-time = "2026-05-06T15:09:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/9e/85/0ef63bcf1337f44031ce9b91b1919563f62a37527b3ea4368bb15a22e5d7/orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd", size = 135188, upload-time = "2026-05-06T15:09:40.957Z" }, + { url = "https://files.pythonhosted.org/packages/05/94/b0d27090ea8a2095db3c2bd1b1c96f96f19bbb494d7fef33130e846e613d/orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62", size = 145937, upload-time = "2026-05-06T15:09:42.249Z" }, + { url = "https://files.pythonhosted.org/packages/09/eb/75d50c29c05b8054013e221e598820a365c8e64065312e75e202ed880709/orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877", size = 132758, upload-time = "2026-05-06T15:09:43.945Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/360686f39348aa88827cb6fbf7dc606fd41c831a35235e1abf1db8e3a9e6/orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1", size = 133971, upload-time = "2026-05-06T15:09:45.239Z" }, + { url = "https://files.pythonhosted.org/packages/0e/30/3178eb16f3221aeef068b6f1f1ebe05f656ea5c6dffe9f6c917329fe17a3/orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd", size = 141685, upload-time = "2026-05-06T15:09:46.858Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f1/ff2f19ed0225f9680fafa42febca3570dd59444ebf190980738d376214c2/orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff", size = 415167, upload-time = "2026-05-06T15:09:48.312Z" }, + { url = "https://files.pythonhosted.org/packages/9b/61/863bddf0da6e9e586765414debd54b4e58db05f560902b6d00658cb88636/orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980", size = 147913, upload-time = "2026-05-06T15:09:49.733Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4081492586d75b073d60c5271a8d0f05a0955cabf1e34c8473f6fcd84235/orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2", size = 136959, upload-time = "2026-05-06T15:09:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bd/70b6ab193594d7abb875320c0a7c8335e846f28968c432c31042409c3c8d/orjson-3.11.9-cp311-cp311-win32.whl", hash = "sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180", size = 131533, upload-time = "2026-05-06T15:09:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/3f/17/1a1a228183d62d1b77e2c30d210f47dd4768b310ebe1607c63e3c0e3a71e/orjson-3.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02", size = 127106, upload-time = "2026-05-06T15:09:54.204Z" }, + { url = "https://files.pythonhosted.org/packages/b8/95/285de5fa296d09681ee9c546cd4a8aeb773b701cf343dc125994f4d52953/orjson-3.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697", size = 126848, upload-time = "2026-05-06T15:09:55.551Z" }, + { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" }, + { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" }, + { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" }, + { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" }, + { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" }, + { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" }, + { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" }, + { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" }, + { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" }, + { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" }, + { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" }, + { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" }, + { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" }, + { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" }, + { url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" }, + { url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" }, + { url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" }, + { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, +] + +[[package]] +name = "pathable" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/55/b748445cb4ea6b125626f15379be7c96d1035d4fa3e8fee362fa92298abf/pathable-0.5.0.tar.gz", hash = "sha256:d81938348a1cacb525e7c75166270644782c0fb9c8cecc16be033e71427e0ef1", size = 16655, upload-time = "2026-02-20T08:47:00.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/96/5a770e5c461462575474468e5af931cff9de036e7c2b4fea23c1c58d2cbe/pathable-0.5.0-py3-none-any.whl", hash = "sha256:646e3d09491a6351a0c82632a09c02cdf70a252e73196b36d8a15ba0a114f0a6", size = 16867, upload-time = "2026-02-20T08:46:59.536Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, + { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, + { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, + { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, + { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, + { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, + { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, + { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, + { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, + { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, + { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, + { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, + { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, + { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/56/030b7b4719d53085722893e0009dffb9236aa10bca1b12121bdc5626ef16/propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b", size = 93417, upload-time = "2026-05-08T20:59:15.597Z" }, + { url = "https://files.pythonhosted.org/packages/1a/55/1140a8e067b8ec093a18a4ae7bb0045d9db65da38a08618ddc5e2f1994aa/propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c", size = 53847, upload-time = "2026-05-08T20:59:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/20/42/0e7443c90310498561addf346e7d57fe3c6ba1914e1ba938b5464c7bbfd2/propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb", size = 53512, upload-time = "2026-05-08T20:59:18.64Z" }, + { url = "https://files.pythonhosted.org/packages/b7/db/cf51a71bab2009517d1a7f0ee07657e3bd446c4d69f67e6966cf17bcf956/propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e", size = 58068, upload-time = "2026-05-08T20:59:20.683Z" }, + { url = "https://files.pythonhosted.org/packages/b7/43/39b6bdee9699fa1e1641c519feeb64a67e2a9f93bb465c70776b37a7333f/propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e", size = 61020, upload-time = "2026-05-08T20:59:22.112Z" }, + { url = "https://files.pythonhosted.org/packages/26/0b/843726fbb0a29a8c5684fdb25971823638399f31e52e9d1f06a02dc9aa6b/propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b", size = 62732, upload-time = "2026-05-08T20:59:23.805Z" }, + { url = "https://files.pythonhosted.org/packages/39/6e/899fed76dc1942b8a64193a4f059d7f1a2c7ef65085e8a9366ed8ec0d199/propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d", size = 60140, upload-time = "2026-05-08T20:59:25.389Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/3da4be9b5b879219ad234aa535b3dd4a080ed1ad48d3a73ca07a9e798f22/propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d", size = 60400, upload-time = "2026-05-08T20:59:27.238Z" }, + { url = "https://files.pythonhosted.org/packages/60/2f/09b72b874a9aa0044faf52a69807a6ed618e267ceaa9ec4a63195fa5b504/propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0", size = 58155, upload-time = "2026-05-08T20:59:28.48Z" }, + { url = "https://files.pythonhosted.org/packages/8a/37/97489848c54c95578045473954f10956d619ce6a09e7ac137b71cdcb698b/propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b", size = 57037, upload-time = "2026-05-08T20:59:30.146Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/6c695285ccfc49012743ee9c98212b8c5dd0aed7b63cfd816d4a0f7a1601/propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf", size = 61103, upload-time = "2026-05-08T20:59:31.626Z" }, + { url = "https://files.pythonhosted.org/packages/98/a9/1e500401ca593b0bdb6bf75a70bc2d723835fd53360edff6af70692c7546/propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf", size = 60394, upload-time = "2026-05-08T20:59:32.829Z" }, + { url = "https://files.pythonhosted.org/packages/1f/87/f638b6e375eae0f30a1a2325d8b34fd85fdc785bb9960cf805f3bf1ec69a/propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e", size = 63084, upload-time = "2026-05-08T20:59:35.964Z" }, + { url = "https://files.pythonhosted.org/packages/f6/18/884573f5d97b6d9eba68de759a82c901b7e39d7904d30f7b8d58d42d2a12/propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274", size = 60999, upload-time = "2026-05-08T20:59:38.481Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1a/c3915eb059ceec9e758a56e4cfd955292bc0f201be2176a46b76d94b303a/propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe", size = 39036, upload-time = "2026-05-08T20:59:40.323Z" }, + { url = "https://files.pythonhosted.org/packages/5b/02/1dfd5607501a602d19c1c449d2d193b7d1c611f9246b4059026a1189a80e/propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d", size = 42190, upload-time = "2026-05-08T20:59:42.232Z" }, + { url = "https://files.pythonhosted.org/packages/57/93/f71588ad08b3e6f4b555b5ef215808a3c02b042d0151ad82fa6f15be677a/propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5", size = 38545, upload-time = "2026-05-08T20:59:44.087Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744, upload-time = "2026-05-08T20:59:45.799Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033, upload-time = "2026-05-08T20:59:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754, upload-time = "2026-05-08T20:59:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573, upload-time = "2026-05-08T20:59:50.778Z" }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645, upload-time = "2026-05-08T20:59:52.227Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563, upload-time = "2026-05-08T20:59:53.866Z" }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888, upload-time = "2026-05-08T20:59:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253, upload-time = "2026-05-08T20:59:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558, upload-time = "2026-05-08T20:59:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007, upload-time = "2026-05-08T20:59:59.837Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355, upload-time = "2026-05-08T21:00:01.144Z" }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057, upload-time = "2026-05-08T21:00:02.401Z" }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938, upload-time = "2026-05-08T21:00:03.638Z" }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731, upload-time = "2026-05-08T21:00:04.881Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966, upload-time = "2026-05-08T21:00:06.511Z" }, + { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135, upload-time = "2026-05-08T21:00:08.088Z" }, + { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381, upload-time = "2026-05-08T21:00:09.692Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "py-key-value-aio" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/3c/0397c072a38d4bc580994b42e0c90c5f44f679303489e4376289534735e5/py_key_value_aio-0.4.4.tar.gz", hash = "sha256:e3012e6243ed7cc09bb05457bd4d03b1ba5c2b1ca8700096b3927db79ffbbe55", size = 92300, upload-time = "2026-02-16T21:21:43.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/69/f1b537ee70b7def42d63124a539ed3026a11a3ffc3086947a1ca6e861868/py_key_value_aio-0.4.4-py3-none-any.whl", hash = "sha256:18e17564ecae61b987f909fc2cd41ee2012c84b4b1dcb8c055cf8b4bc1bf3f5d", size = 152291, upload-time = "2026-02-16T21:21:44.241Z" }, +] + +[package.optional-dependencies] +filetree = [ + { name = "aiofile", version = "3.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "aiofile", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "anyio" }, +] +keyring = [ + { name = "keyring" }, +] +memory = [ + { name = "cachetools" }, +] + +[[package]] +name = "pyarrow" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/bf/a34fee1d624152124fa8355c42f34195ad5fe5233ce5bb87946432047d52/pyarrow-24.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb", size = 35076681, upload-time = "2026-04-21T08:51:46.845Z" }, + { url = "https://files.pythonhosted.org/packages/1d/41/64180033d7027afce12dc96d0fe1f504c6fa112190582b458acea2399530/pyarrow-24.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147", size = 36684260, upload-time = "2026-04-21T08:51:53.642Z" }, + { url = "https://files.pythonhosted.org/packages/57/02/9b9320e673dd8a99411fac78690f3df92f6dd6f59754c750110bca66d64e/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c", size = 45698566, upload-time = "2026-04-21T10:46:02.133Z" }, + { url = "https://files.pythonhosted.org/packages/67/33/f75e91b9a64c3f33c787e263c93b871ad91b8a4a68c1d5cebddd9840e835/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041", size = 48835562, upload-time = "2026-04-21T10:46:10.278Z" }, + { url = "https://files.pythonhosted.org/packages/a5/63/097510448e47e4091faa41c43ba92f97cecaab8f4535b56a3d149578f634/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491", size = 49394997, upload-time = "2026-04-21T10:46:18.08Z" }, + { url = "https://files.pythonhosted.org/packages/60/6b/c047d6222ab279024a062742d1807e2fbaf27bba88a98637299ff47b9236/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1", size = 51911424, upload-time = "2026-04-21T10:46:25.347Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ba/464cc70761c2a525d97ebd84e21c31ebd47f3ef4bdcee117009f51c46f24/pyarrow-24.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591", size = 27251730, upload-time = "2026-04-21T10:46:30.913Z" }, + { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898, upload-time = "2026-04-21T10:46:36.599Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915, upload-time = "2026-04-21T10:46:42.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931, upload-time = "2026-04-21T10:46:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449, upload-time = "2026-04-21T10:46:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949, upload-time = "2026-04-21T10:47:01.869Z" }, + { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986, upload-time = "2026-04-21T10:47:09.872Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371, upload-time = "2026-04-21T10:47:15.943Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997, upload-time = "2026-04-21T10:49:48.796Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720, upload-time = "2026-04-21T10:49:55.858Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852, upload-time = "2026-04-21T10:50:04.624Z" }, + { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852, upload-time = "2026-04-21T10:50:12.293Z" }, + { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207, upload-time = "2026-04-21T10:50:20.677Z" }, + { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117, upload-time = "2026-04-21T10:50:29.14Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155, upload-time = "2026-04-21T10:51:22.337Z" }, + { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387, upload-time = "2026-04-21T10:50:35.552Z" }, + { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102, upload-time = "2026-04-21T10:50:42.417Z" }, + { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118, upload-time = "2026-04-21T10:50:49.324Z" }, + { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765, upload-time = "2026-04-21T10:50:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890, upload-time = "2026-04-21T10:51:02.439Z" }, + { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250, upload-time = "2026-04-21T10:51:10.576Z" }, + { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282, upload-time = "2026-04-21T10:51:16.815Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, +] + +[[package]] +name = "pydub" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326, upload-time = "2021-03-10T02:09:54.659Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327, upload-time = "2021-03-10T02:09:53.503Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/54/a85eb421fbdd5007bc5af39d0f4ed9fa609e0fedbfdc2adcf0b34526870e/python_multipart-0.0.28.tar.gz", hash = "sha256:8550da197eac0f7ab748961fc9509b999fa2662ea25cef857f05249f6893c0f8", size = 45314, upload-time = "2026-05-10T11:05:16.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/a2/43bbc5860b5034e2af4ef99a0e04d726ff329c43e192ef3abaa8d7ecfce5/python_multipart-0.0.28-py3-none-any.whl", hash = "sha256:10faac07eb966c3f48dc415f9dee46c04cb10d58d30a35677db8027c825ed9b6", size = 29438, upload-time = "2026-05-10T11:05:15.052Z" }, +] + +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "regex" +version = "2026.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ed/0ad2c8edf634918eb4484365d3819fa7bd7f58daf807fe7fb21812c316e5/regex-2026.5.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a9e1328e17c84c1a5d22ec9f785ecef4a967fab9a42b6a8dc3bcbebd0a0c9e44", size = 489438, upload-time = "2026-05-09T23:11:29.374Z" }, + { url = "https://files.pythonhosted.org/packages/89/a9/4ed972ad263963b860b7c3e86e0e1bcc791def47b43b8c8efe57e710f139/regex-2026.5.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfe1ce50cbfb569d74e1e4337da6468961f31dbea55fd85aa5de59c0947a805a", size = 291270, upload-time = "2026-05-09T23:11:33.254Z" }, + { url = "https://files.pythonhosted.org/packages/16/81/075930d9fa28c4ea1f53398dd015ee7c882f623539759113cda1257f4b82/regex-2026.5.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15ee42209947f4ca045412eae98416317238163618ace2a8e54f99586a466733", size = 289198, upload-time = "2026-05-09T23:11:35.769Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c8/5cdfbf0b5dc6599e1b6131eff43262e5275d4ec3469ce10216061659aadb/regex-2026.5.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4bb445ff3f725f59df8f6014edb547ee928ec7023a774f6a39a3f953038cbb2", size = 784765, upload-time = "2026-05-09T23:11:37.689Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ca/ae5fd6edc59b7f84b904b31d6ec39a860cbcecd10f64bd5a062ca83a4864/regex-2026.5.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:446ddd671e43ab535810c4b21cff7104945c701d4a14d1e6d1cd6f4e445a8bea", size = 852115, upload-time = "2026-05-09T23:11:39.973Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ce/a91cf555afb51f3b74a182e24ba073b91ea7bb64592fc4b315c111bb19fd/regex-2026.5.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b92817338591505f282cf3864c145244b1edcf5381d237038df955001091538", size = 899503, upload-time = "2026-05-09T23:11:42.48Z" }, + { url = "https://files.pythonhosted.org/packages/55/7f/725a0a2b245a4cf0c4bab29d0e97c74285d94136a65d1b55a6459a583502/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b8a143aca6c39b446ea8092cde25cc8fe9304d4f5fecfbc1a9dbb0282703c2", size = 794093, upload-time = "2026-05-09T23:11:44.681Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2a/996efbd59ce6b5d4a09e3af6180ceb62af171f4a9a6fb557d2f0ae0d462b/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0f03aa6898aaaac4592479821df16e68e8d0e29e903e65d8f2dfb2f19028a989", size = 786234, upload-time = "2026-05-09T23:11:46.882Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/8731e8b8806174c9cdd5903f80a14990331c1f42fc4209b540952e9e010d/regex-2026.5.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ed457d8e98ae812ed7732bef7bf78de78e834eae0372a74e23ca90ef21d910f9", size = 769895, upload-time = "2026-05-09T23:11:49.324Z" }, + { url = "https://files.pythonhosted.org/packages/9a/0b/932473194bd563f342a412ae2ffbbd6da608306a2bc4e99249a41c2b0b92/regex-2026.5.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71b61c5bfe1c806332defc42ad6c780b3c55f661986d7f40283a3a88274b4c00", size = 774991, upload-time = "2026-05-09T23:11:51.261Z" }, + { url = "https://files.pythonhosted.org/packages/98/80/9523d196010031df25f7177ee0a467efbee436324038e5d99def17a57515/regex-2026.5.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3b1e39888c5e0c7d92cea4fc777396c4a90363b05de75d02eb459a4752200808", size = 848790, upload-time = "2026-05-09T23:11:53.232Z" }, + { url = "https://files.pythonhosted.org/packages/3c/07/56987b35e89edf47e4a38cf2845aeee476bfa688a6bdbd3e820cda461dc1/regex-2026.5.9-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6ba42b2e7e7f46cf68cc6a5ca36fa07959f9bbd9c6bdcc47b6ee76549a590248", size = 757679, upload-time = "2026-05-09T23:11:55.82Z" }, + { url = "https://files.pythonhosted.org/packages/04/2a/ff713fff0c566507c06a4ce2dc0ae8e7eeebc88811a95fc81cf1e7d534dd/regex-2026.5.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c010eb8caca74bdb40c07498d7ece26b4428fd3f04aa8a72c9ac6f79e8faaac6", size = 837116, upload-time = "2026-05-09T23:11:57.934Z" }, + { url = "https://files.pythonhosted.org/packages/77/90/df6d982b03e3614785c6937ba51b57f6733d97d2ee1c9bc7531dbfab3a54/regex-2026.5.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a6a563446a41adc451393dc6b8e6ad87979efaee3c8738690a8d1b08ebead1b4", size = 782081, upload-time = "2026-05-09T23:11:59.607Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/4e88a5f7c3e98489aac4dd23142723d907b2a595b4a6abcbacabefeded09/regex-2026.5.9-cp310-cp310-win32.whl", hash = "sha256:954cc214c04663ee6d266fc61739cad83054683048de65c5bd1d640ad28098ac", size = 266247, upload-time = "2026-05-09T23:12:01.116Z" }, + { url = "https://files.pythonhosted.org/packages/6a/40/4b224cb0582b2dca1786726e6cdabe26abbf757d7f6718332f186da155d2/regex-2026.5.9-cp310-cp310-win_amd64.whl", hash = "sha256:b310768746dd314ea6e2ff4cc89ef215426813396ff4e94ee8e6f7096c8b6e03", size = 278416, upload-time = "2026-05-09T23:12:03.2Z" }, + { url = "https://files.pythonhosted.org/packages/12/4d/014fbe803204cab0947ee428f09f658a29632053dde1d3c6176bb4f0fd4c/regex-2026.5.9-cp310-cp310-win_arm64.whl", hash = "sha256:19c16ceb4a267a8789e25733e583983eeab9f0f8664e66b0bd1c5d21f14c2d4b", size = 270413, upload-time = "2026-05-09T23:12:04.649Z" }, + { url = "https://files.pythonhosted.org/packages/c2/dc/c1f2df4027e82fc54b5a473e4b250f5139faca49a0fbe29a48668d228f34/regex-2026.5.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48", size = 489445, upload-time = "2026-05-09T23:12:06.111Z" }, + { url = "https://files.pythonhosted.org/packages/03/d2/59f01110660081cce9c0bc30ebd0b5ee250dacf658e3248ed92f01e0e8ee/regex-2026.5.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8", size = 291271, upload-time = "2026-05-09T23:12:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/58/b6/14b2c84ff90ddb370c81d27503f4a0fcf071496416f4855f6cc8c5d81c35/regex-2026.5.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555", size = 289212, upload-time = "2026-05-09T23:12:09.266Z" }, + { url = "https://files.pythonhosted.org/packages/03/d0/4db86529117320de0c84afd90e70bb47434625875e34fcef9d8c127c5b16/regex-2026.5.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919", size = 792310, upload-time = "2026-05-09T23:12:11.416Z" }, + { url = "https://files.pythonhosted.org/packages/07/78/fe4800cd322f862ecffd2d553409b20d80650e5ed71b9d178f853d020b82/regex-2026.5.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451", size = 861721, upload-time = "2026-05-09T23:12:13.681Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d0/b3618a895dd8feb897c61bb2954edd265e1767d82a01d53065d5871127a3/regex-2026.5.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c", size = 906460, upload-time = "2026-05-09T23:12:15.443Z" }, + { url = "https://files.pythonhosted.org/packages/33/6f/1481597e859ef19508b345eec4afd1416ed6e6b459c75a64026ef193aecf/regex-2026.5.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc", size = 799843, upload-time = "2026-05-09T23:12:16.892Z" }, + { url = "https://files.pythonhosted.org/packages/73/59/955734c803f59108deccba3597ae440c76b62a652733c0006e6243758420/regex-2026.5.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d", size = 773610, upload-time = "2026-05-09T23:12:19.127Z" }, + { url = "https://files.pythonhosted.org/packages/68/8f/70c04a236d651c81881dac42ef8538bddda6121434509d0a22d9e601503b/regex-2026.5.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9", size = 781645, upload-time = "2026-05-09T23:12:20.806Z" }, + { url = "https://files.pythonhosted.org/packages/1d/96/05c7434d88185e5d27fe54aeb74df86bd77cd79f52f0b4eae54faa8fea70/regex-2026.5.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2", size = 854473, upload-time = "2026-05-09T23:12:22.465Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/6e3d8202d981f3117004bf341ee74893ba4ba8a9fbaf4b94615846550a08/regex-2026.5.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf", size = 763311, upload-time = "2026-05-09T23:12:24.351Z" }, + { url = "https://files.pythonhosted.org/packages/93/c7/e7737f1526b3fb32bd4c337fd6c71c3ebb5c8296fc34d11197e0955d2e35/regex-2026.5.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611", size = 844593, upload-time = "2026-05-09T23:12:26.341Z" }, + { url = "https://files.pythonhosted.org/packages/a5/27/0daffb1a535bb39f422c3d200f4ab023c71110ad66a32b366bee708baba0/regex-2026.5.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c", size = 789167, upload-time = "2026-05-09T23:12:27.975Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fc/294fe4fac4f2ed67207b17471815870c1c45b3a489e08e0ac96daea16ef6/regex-2026.5.9-cp311-cp311-win32.whl", hash = "sha256:8676474c07469d6f33dd1085ca2cd45f65785f32518f2b20e36d9953ca07f994", size = 266249, upload-time = "2026-05-09T23:12:30.141Z" }, + { url = "https://files.pythonhosted.org/packages/d0/b0/8dce459f6245bcf8f6e9f23ac9569f1a0f15c131cc0745e82b43226204cf/regex-2026.5.9-cp311-cp311-win_amd64.whl", hash = "sha256:246de9d60aa3f8538b519834dd95cbf276ea263d6a7bd5a3666dc3fa0230505b", size = 278423, upload-time = "2026-05-09T23:12:31.676Z" }, + { url = "https://files.pythonhosted.org/packages/db/8d/f9aeff6ad63a3ef720386f2907e6d34a35a510a6e498ebad28b0fb3f6ab6/regex-2026.5.9-cp311-cp311-win_arm64.whl", hash = "sha256:d726ca3f0d76969bf1e8e477d160d3d666bbf999f6860bd314889e5345782046", size = 270420, upload-time = "2026-05-09T23:12:33.194Z" }, + { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, + { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, + { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, + { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, + { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, + { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, + { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, + { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, + { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, + { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, + { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, + { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, + { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, + { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, + { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, + { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, + { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, + { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rich-rst" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, +] + +[[package]] +name = "safehttpx" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/d1/4282284d9cf1ee873607a46442da977fc3c985059315ab23610be31d5885/safehttpx-0.1.7.tar.gz", hash = "sha256:db201c0978c41eddb8bb480f3eee59dd67304fdd91646035e9d9a720049a9d23", size = 10385, upload-time = "2025-10-24T18:30:09.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/a3/0f0b7d78e2f1eb9e8e1afbff1d2bff8d60144aee17aca51c065b516743dd/safehttpx-0.1.7-py3-none-any.whl", hash = "sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde", size = 8959, upload-time = "2025-10-24T18:30:08.733Z" }, +] + +[[package]] +name = "safetensors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, +] + +[[package]] +name = "secretstorage" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "jeepney", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, +] + +[[package]] +name = "semantic-version" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/31/f2289ce78b9b473d582568c234e104d2a342fd658cc288a7553d83bb8595/semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c", size = 52289, upload-time = "2022-05-26T13:35:23.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552, upload-time = "2022-05-26T13:35:21.206Z" }, +] + +[[package]] +name = "setuptools" +version = "81.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/2b/58abc2d1fd397e7dde08e947e05c884d8ef2f78d5e2588c17a12d42d6994/sse_starlette-3.4.4.tar.gz", hash = "sha256:07e0fa0460138baf25cdd5fb28683472c3995dc1642225191b3832d62526bcb0", size = 31819, upload-time = "2026-05-12T17:37:17.019Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/67/805710444ea8cc75fbf70b920ed431a560c4bf9c57f7d5a3117213189399/sse_starlette-3.4.4-py3-none-any.whl", hash = "sha256:3f4dd50d8aed2771a091f3a83000323fc3844541c16b4fe585ae2420cc6df973", size = 16514, upload-time = "2026-05-12T17:37:15.601Z" }, +] + +[[package]] +name = "starlette" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, +] + +[[package]] +name = "torch" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/b7/53fe0436586716ab7aecff41e26b9302d57c85ded481fd83a2cd741e6b4e/torch-2.12.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1834bd984f8a2f4f16bdfbeecca9146184b220aa46276bf5756735b5dae12812", size = 87981887, upload-time = "2026-05-13T14:55:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/34/60/d930eac44c30de06ed16f6d1ba4e785e1632532b50d8f0bf9bf699a4d0c7/torch-2.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d4d029801cb7b6df858804a2a21b00cc2aa0bf0ee5d2ab18d343c9e9e5681f35", size = 426355000, upload-time = "2026-05-13T14:54:31.944Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0c/c76b6a087820bab55705b94dfc074e520de9ae91f5ef90da2ecbf2a3ef12/torch-2.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d47e7dee68ac4cd7a068b26bcd6b989935427709fae1c8f7bd0019978f829e15", size = 532144998, upload-time = "2026-05-13T14:56:05.523Z" }, + { url = "https://files.pythonhosted.org/packages/4a/64/8a0d036e166a6aa85ee09bef072f3655d1ba5d5486a68d1b03b6813c01b3/torch-2.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:cf9839790285dd472e7a16aafcb4a4e6bf58ec1b494045044b0eefb0eb4bd1f2", size = 122949877, upload-time = "2026-05-13T14:55:46.841Z" }, + { url = "https://files.pythonhosted.org/packages/18/62/131124fb95df03811b8260d1d43dcc5ee85ea1a344b964613d7efe77fb08/torch-2.12.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:10802fd383bbfed646212e765a72c37d2185205d4f26eb197a254e8ac7ddcb25", size = 87990344, upload-time = "2026-05-13T14:55:42.154Z" }, + { url = "https://files.pythonhosted.org/packages/12/9c/dda0dbd547dc549839824135f223792fd0e725f28ed0715dda366b7acaa2/torch-2.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c12592630aef72feaf18bd3f197ef587bbfa21131b31c38b23ab2e55fce92e36", size = 426362932, upload-time = "2026-05-13T14:54:15.295Z" }, + { url = "https://files.pythonhosted.org/packages/e2/d2/a7dd5a3f9bdaa7842124e8e2359202b317c48d47d2fc5816fafdf2049adb/torch-2.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:415c1b8d0412f67551c8e89a2daca0fb3e56694af0281ba155eaa9da481f58b4", size = 532170085, upload-time = "2026-05-13T14:55:20.788Z" }, + { url = "https://files.pythonhosted.org/packages/12/1b/a61ce2004f9ab0ea8964a6e6168133a127795667639e2ff4f8f2bdb16a65/torch-2.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd37188ea325042cb1f6cafa56822b11ada2520c04791a52629b0af25bdfbfd9", size = 122953128, upload-time = "2026-05-13T14:54:52.744Z" }, + { url = "https://files.pythonhosted.org/packages/ef/bb/285d643f254731294c9b595a007eac39db4600a98682d7bca688f42ca164/torch-2.12.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b41339df93d491435e790ff8bcbae1c0ce777175889bfd1281d119862793e6a2", size = 88010197, upload-time = "2026-05-13T14:55:35.414Z" }, + { url = "https://files.pythonhosted.org/packages/79/81/76debf1db1343bd929bbb5d74c89fb437c2ed88eb144712557e7bd3eea45/torch-2.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8fbef9f108a863e7722a73740998967e3b074742a834fc5be3a535a2befa7057", size = 426376751, upload-time = "2026-05-13T14:55:03.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/80026028b603c4650ff270fc3785bdef4bd6738765a9cc5a0f5a637d65a2/torch-2.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b4f64c2c2b11f7510d93dd6412b87025ff6eddd6bb61c3b5a3d892ea20c4756", size = 532261691, upload-time = "2026-05-13T14:52:54.453Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c2/64b06cbb7830fb3cd9be13e1158b31a3f36b68e6a209105ee3c9d9480be0/torch-2.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b958caff4a14d3a3b0b2dfc6a378f64dda9728a9dad28c08a0db9ce4dafb549", size = 122988114, upload-time = "2026-05-13T14:54:42.153Z" }, + { url = "https://files.pythonhosted.org/packages/86/ca/01896c80ba921676aa45886b2c5b8d774912de2a1f719de48169c6f755cd/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", size = 88009511, upload-time = "2026-05-13T14:54:47.411Z" }, + { url = "https://files.pythonhosted.org/packages/a5/04/52bdaf4787eab6ac7d7f5851dff934e4def0bc8ead9c8fd2b69b3e529699/torch-2.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:864392c73b7654f4d2b3ae712f607937d0dbb1101c4555fbb41848106b297f39", size = 426383231, upload-time = "2026-05-13T14:53:32.129Z" }, + { url = "https://files.pythonhosted.org/packages/49/8a/94bdecd13f5aaa90d45920b89789d9fe7c6f4af8c3cdd7ce01fcb59908fc/torch-2.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d6b560dfa7d56291c07d615c3bb73e8d9943d9b6d87f76cd0d9d570c4797fa6", size = 532269288, upload-time = "2026-05-13T14:53:49.423Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2f/bdbaaa267de519ef1b73054bf590d8c93c37a266c9a4e24a01bd38b6918f/torch-2.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:3fee918902090ade827643e758e98363278815de583c75d111fdd665ebffde9f", size = 122987706, upload-time = "2026-05-13T14:54:00.335Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ad/e95e822f3538171e22640a7fbe839a1fdb666600bf6487025de2ff03b11a/torch-2.12.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:10ee1448a9f304d3b987eb4656f664ba6e4d7b410ca7a5a7c642199777a2cf88", size = 88319556, upload-time = "2026-05-13T14:54:05.574Z" }, + { url = "https://files.pythonhosted.org/packages/b7/07/055d06d985b445d67422d25b033c11cf55bbb81785d4c4e68e28bca5820e/torch-2.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af68dbf403439cae9ceaeaaf92f8352b460787dcd27b92aa05c40dd4a19c0f1e", size = 426397656, upload-time = "2026-05-13T14:52:38.84Z" }, + { url = "https://files.pythonhosted.org/packages/43/94/b0b4fdc3014122e0a7302fb90086d352aa48f2576f0b252561ebb38c01a8/torch-2.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a6a2eebb237d3b1d9ad3b378e86d9b9e0782afdea8b1e0eba6a13646b9b49c07", size = 532183124, upload-time = "2026-05-13T14:53:16.178Z" }, + { url = "https://files.pythonhosted.org/packages/d8/c8/052405e6ad05d3237bfe5a4df78f917773956f8e17813a2d44c059068b74/torch-2.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2140e373e9a51a3e22ef62e8d14366d0b470d18f0adf19fdc757368077133a34", size = 123232462, upload-time = "2026-05-13T14:52:27.26Z" }, + { url = "https://files.pythonhosted.org/packages/67/dc/ac069f8d6e8be701535921141055293b0d4819d3d7f224a4612cf157c7f9/torch-2.12.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7dfae4a519197dfa050e98d8e36378a0fb5899625a875c2b54445005a2e404e", size = 88027282, upload-time = "2026-05-13T14:53:05.258Z" }, + { url = "https://files.pythonhosted.org/packages/33/c3/1c1eb00e34555b536dddf792676026a988d710ed36981aa00499b36b0620/torch-2.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:891c769072637c74e9a5a77a3bc782894696d8ffec83b938df8536dee7f0ba78", size = 426386961, upload-time = "2026-05-13T14:51:28.406Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d4/7e730dba0c7032a4154dc9056b76cf9625515e030e269cfbf8098fcfee7d/torch-2.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e2ad3eb85d39c3cab62dfa93ed5a73516e6a53c6713cb97d004004fe089f0f1f", size = 532272265, upload-time = "2026-05-13T14:51:59.308Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b4/92c80d1bbfee1c0036c06d1d2155a3065bd2423134c83bf8a47e65cd6b9b/torch-2.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:c66696857e987efb8bc1777a37357ec4f60ab5e8af6250b83d6034437fa2d8f3", size = 122987138, upload-time = "2026-05-13T14:51:45.942Z" }, + { url = "https://files.pythonhosted.org/packages/7b/78/2e12b37ce50a19a037d7bc62d652a5a8f27385a7b05859d6bc9204f20cfe/torch-2.12.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b4556715c8572758625d62b6e0ae3b1f76c440221913a6fb5e100f321fb4fb02", size = 88320100, upload-time = "2026-05-13T14:51:39.955Z" }, + { url = "https://files.pythonhosted.org/packages/56/5e/83c450ec7b0bb40a7b74611c1b5440f9260e33c54c90d556fd4a1f0fd955/torch-2.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a43ac605a5e13116c72b64c359644cce0229f213dde48d2ae0ae5eb5becf7feb", size = 426391871, upload-time = "2026-05-13T14:52:14.989Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e9/1a0b575d98d0afedd8f157d23fa3d2759421483660448e60d0a4b10b6daa/torch-2.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a7512adfdd7f6732e40de1c620831e3c75b39b98cef60b11d0c5f0a76473ec5", size = 532192241, upload-time = "2026-05-13T14:51:07.795Z" }, + { url = "https://files.pythonhosted.org/packages/88/21/afadd25ecd81b3cea1e11c73cf1ab41a983a50271548c3ec7ec3b9efc3e9/torch-2.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f96b63f8287f66a005dd1b5a6abba2920f11156c5e5c4d815f3e2050fd1aa16", size = 123231092, upload-time = "2026-05-13T14:51:18.854Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "trackio" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gradio-client" }, + { name = "huggingface-hub" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "orjson" }, + { name = "pillow" }, + { name = "python-multipart" }, + { name = "starlette" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "uvicorn", extra = ["standard"] }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9a/80caa250ef9e0e465db883958764c9b27a413c8b246349f60c237b820201/trackio-0.25.1-py3-none-any.whl", hash = "sha256:d019b79a8dfb14264db1055b7e9b2681984138b827dabd5ec01585c0bd24116d", size = 1653922, upload-time = "2026-04-26T02:13:41.814Z" }, +] + +[[package]] +name = "transformers" +version = "5.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/e6/4134ea2fbea322cddc7ffc94a0d8ee47fe32ce8e876b320cd37d88edfc4d/transformers-5.8.1.tar.gz", hash = "sha256:4dd5b6de4105725104d84fd6abd74b305f4debfc251b38c648ee5dd087cf543b", size = 8532019, upload-time = "2026-05-13T03:21:57.234Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/b1/8be7e7ef0b5200491312201918b6125ef9c9df9dd0f0240ccef9ac824e6b/transformers-5.8.1-py3-none-any.whl", hash = "sha256:5340fb95962162cdfdae5cc91d7f8fedd92ed75216c1154c5e1f590fcf56dd0e", size = 10632882, upload-time = "2026-05-13T03:21:52.876Z" }, +] + +[[package]] +name = "triton" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/97/dcd1f2a0f8336691bff74abc59b2ed9c69a0c0f8f65cd77109c49e05f068/triton-3.7.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223ac302091491436c248a34ee1e6c47a1026486579103c906ffd805be50cb89", size = 188367104, upload-time = "2026-05-07T19:04:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c0/c2ac4fd2d8809b7579d4a820a0f9e5de62a9bc8a757ed4b3abf4f7ee964a/triton-3.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c631b65668d4951213b948a413c0564184305b77bb45cc9d686d3e1ecc4701a3", size = 201313191, upload-time = "2026-05-07T18:45:58.444Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c1/5d842314bb6c78442cc60437928781701c6050b8d479bc2a1aed691d37ca/triton-3.7.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9e71fc392675fac364e0ecf4ef3f76f85b7f5433a16f4c3c5fe5f05a52c85fe", size = 188480277, upload-time = "2026-05-07T19:05:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/13/31/8315ea5f8dd18e60970b3022e3a8b93fd37e0b784fbbef86e10c8e6e5ca1/triton-3.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22bacffce443f54593dd20f05294d5a40622e0ea9ab632816f87154504356221", size = 201415942, upload-time = "2026-05-07T18:46:06.479Z" }, + { url = "https://files.pythonhosted.org/packages/f7/13/ec05adfcd87311d532ba61e3af143e8be59fcd26675884c4682841406a20/triton-3.7.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4bf49b00a7a377a68a6da603a876e797614e6455a80e9021669c476a953ad9a", size = 188505104, upload-time = "2026-05-07T19:05:09.843Z" }, + { url = "https://files.pythonhosted.org/packages/62/7b/468a576e35beef1426e0828e28e9ba9e65f5474d496f16ee126c15646324/triton-3.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f111161d49bf903c0eaedde3962353a3d841c08a836839b7cc1025b8426efcf", size = 201457567, upload-time = "2026-05-07T18:46:13.505Z" }, + { url = "https://files.pythonhosted.org/packages/01/e1/a59a583de59b8f62c495d67c80ee3ea97d09e91ac80c4c6e76456ed8d8ac/triton-3.7.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abdf6beaa89b1bcfb9a43cd990536ce66091a997841a4814b260b7bee4c88c3c", size = 188503209, upload-time = "2026-05-07T19:05:17.935Z" }, + { url = "https://files.pythonhosted.org/packages/30/b1/b7507bb9815d403927c8dd51d4158ed2e11751a92dbc118a044f247b6848/triton-3.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a35d7afe3f3f058e7ec49fcce09794049e0ffc5c59019ac25ec3413741b8c4e7", size = 201453566, upload-time = "2026-05-07T18:46:20.427Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8f/0bea7a6a0c989315c9135a1d7fb37e41905cfb3a17cbc1f10044ebd4cc3a/triton-3.7.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc1d61c172d257db80ddf42595131fb196ad2e9bdd751e90fe2ef13531734e8b", size = 188612899, upload-time = "2026-05-07T19:05:24.955Z" }, + { url = "https://files.pythonhosted.org/packages/e1/02/d96f57828d0912aec733b9bc7e0e7dbfd2c6f079a8fa433ac25cb93d1a30/triton-3.7.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70fb9bbdc9f400afc54bbf6eb2670af28829a6ae3996863317964783141daf56", size = 201553816, upload-time = "2026-05-07T18:46:27.49Z" }, + { url = "https://files.pythonhosted.org/packages/40/fb/82a802dac4689f2a2fb2e69302e6a138eecc3e175bbe976ba3cfc717683a/triton-3.7.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a44a8476d0d3571eac4e4d1048e1ff75aad81a09ff4602ccfc56c6dea1672e", size = 188507879, upload-time = "2026-05-07T19:05:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/8f/af/9904ec6d3c93d9b24e5ec360445bbdf758b7f00bfbeedb89cb0eb64eb8bb/triton-3.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b85e72968a9d8bba5ddb24e9b64aaabaf48affb042f2755cb7cfa92b7531ce", size = 201460637, upload-time = "2026-05-07T18:46:34.749Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f9/4835a8ea746b88727d8899f4e3ccce4f9cacb38abfc3bb0a638266c53111/triton-3.7.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a160de426fd99f92b0baf509045360afbd3bfaa0b4a5171dde800ec9f09684", size = 188608706, upload-time = "2026-05-07T19:05:39.218Z" }, + { url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766, upload-time = "2026-05-07T18:46:42.088Z" }, +] + +[[package]] +name = "trl" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accelerate" }, + { name = "datasets" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/fe/eaa56e5d1bb7c418340dcc97c7d1a724dda0d42419e1b3c16c230af942b1/trl-1.4.0.tar.gz", hash = "sha256:37f9b9a4401922469f02be85c6230ddfc680d746338c5d3f6fc5641fbff83908", size = 619070, upload-time = "2026-05-08T23:24:14.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/f4/74bf11119cad07b3cd72b0c7213c5e90b17025b4af429418b72314b245bc/trl-1.4.0-py3-none-any.whl", hash = "sha256:d8c1aeaa196cdaf2156206aa087d8c332c59e197d70b07585a1228abc19822c6", size = 751040, upload-time = "2026-05-08T23:24:12.989Z" }, +] + +[[package]] +name = "typer" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +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/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]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "uncalled-for" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/82/345cc927f7fbdae6065e7768759932fcc827fc20b29b45dfbafa2f1f7da4/uncalled_for-0.3.2.tar.gz", hash = "sha256:89f5dbcd71e2b8f47c030b1fa302e6cce2ec795d1ac565eeb6525c5fe55cb8a2", size = 50032, upload-time = "2026-05-06T13:38:25.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/25/2c87754f3a9e692315f7b811244090e68f362979fc8886b3fbd2985a1d8c/uncalled_for-0.3.2-py3-none-any.whl", hash = "sha256:0ff60b142c7d1f8070bde9d42afaa70aedc77dcc10998c227687e9c15713418e", size = 11444, upload-time = "2026-05-06T13:38:24.025Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.47.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/b1/8e7077a8641086aea449e1b5752a570f1b5906c64e0a33cd6d93b63a066b/uvicorn-0.47.0.tar.gz", hash = "sha256:7c9a0ea1a9414106bbab7324609c162d8fa0cdcdcb703060987269d77c7bb533", size = 90582, upload-time = "2026-05-14T18:16:54.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/41/ac2dfdbc1f60c7af4f994c7a335cfa7040c01642b605d65f611cecc2a1e4/uvicorn-0.47.0-py3-none-any.whl", hash = "sha256:2c5715bc12d1892d84752049f400cd1c3cb018514967fdfeb97640443a6a9432", size = 71301, upload-time = "2026-05-14T18:16:51.762Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" }, + { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" }, + { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" }, + { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, + { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, + { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, + { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, + { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, + { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, + { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, + { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, + { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, + { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, + { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, + { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, +] + +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343, upload-time = "2026-01-10T09:22:21.28Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021, upload-time = "2026-01-10T09:22:22.696Z" }, + { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320, upload-time = "2026-01-10T09:22:23.94Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815, upload-time = "2026-01-10T09:22:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054, upload-time = "2026-01-10T09:22:27.101Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565, upload-time = "2026-01-10T09:22:28.293Z" }, + { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848, upload-time = "2026-01-10T09:22:30.394Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249, upload-time = "2026-01-10T09:22:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685, upload-time = "2026-01-10T09:22:33.345Z" }, + { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, + { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/49/e4b575b4ed170a7f640c8bd69cfadfa81c7b700191fde5e72228762b9f73/xxhash-3.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd8ab85c916a58d5c8656ea15e3ce9df836fe2f120a74c296e01d69fab2614b4", size = 33426, upload-time = "2026-04-25T11:05:15.702Z" }, + { url = "https://files.pythonhosted.org/packages/07/61/40f0155b0b09988eb6cdbfc52652f2f371810b0c58163208cb05667757bd/xxhash-3.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85f5c0e26d945b5bb475e0a3d95193117498130baa7619357bdc7869c2391b5a", size = 30859, upload-time = "2026-04-25T11:05:17.708Z" }, + { url = "https://files.pythonhosted.org/packages/12/bd/2902b7aad574e43cd85fd84849cfbce48c52cb02c7d6902b8a2b3f6e668e/xxhash-3.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b7ffeaada9f8699be63d639536b0b60dff73b7d3325b7475c5bc8fdbf4eed47f", size = 193839, upload-time = "2026-04-25T11:05:19.364Z" }, + { url = "https://files.pythonhosted.org/packages/48/df/343ce8fd09e47ba8fba43b3bad3283ddf0deca799d5a27b084c3aa2ce502/xxhash-3.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee88dfaa6b1b2bfadd3c031fa5f05584870e62fb05dc500942e9900c44fcfda", size = 212896, upload-time = "2026-04-25T11:05:21.131Z" }, + { url = "https://files.pythonhosted.org/packages/79/cf/703e8422a8b52407864281fb4eb52c605e9f33180413b4458f05de110eba/xxhash-3.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7426ff0dfa76eb47efc2cc59d4a717bfa9dc9938bff5e49e748bca749f6aa616", size = 235896, upload-time = "2026-04-25T11:05:22.988Z" }, + { url = "https://files.pythonhosted.org/packages/ed/bc/d4b039edbd426575add5f217abeeb2bf870e2c510d35445df81b4f457901/xxhash-3.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8ff6ec73110f610425caef3ea875afbfc34caa542f01df3a80f45aadeb9f906", size = 211665, upload-time = "2026-04-25T11:05:24.799Z" }, + { url = "https://files.pythonhosted.org/packages/42/24/c6f81361796814b92399a88bf079d3b65e617f531819128fcf1bd6ef0571/xxhash-3.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d23fd49fdc5c8af61fb7104f1ad247954499140f6cb6045b3aa5c99dadbbf28", size = 444929, upload-time = "2026-04-25T11:05:26.245Z" }, + { url = "https://files.pythonhosted.org/packages/a4/db/268012153eb7f6bf2c8a0491fdcde11e093f166990821a2ab754fe95537d/xxhash-3.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c249621af6d50a05d9f10af894b404157b15819878e18f75fcbb0213a77d07", size = 193271, upload-time = "2026-04-25T11:05:28.282Z" }, + { url = "https://files.pythonhosted.org/packages/0a/86/1d0d905d659850dad7f59c807c130249fdb204dc6f71f1fb36268f3f3e61/xxhash-3.7.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6741564a923f082f3c2941c8bb920462ed5b25eaebdd1e161f162233c9a10bc5", size = 284580, upload-time = "2026-04-25T11:05:30.116Z" }, + { url = "https://files.pythonhosted.org/packages/1f/52/fc01ca7ff425a9bdb38d9e3a17f2630447ce3b45d45a929a6cd94d469334/xxhash-3.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4fd8acc6e32596350619896feb372033c0920975992d29837c32853bb1feacd", size = 210193, upload-time = "2026-04-25T11:05:31.969Z" }, + { url = "https://files.pythonhosted.org/packages/ec/96/122e0c6a3537a54b30752031dca557182576bae1a4171c0be8c532c84496/xxhash-3.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:646a69b56d8145d85f7fd2289d14fba07880c8a5bda406aa256b407481a61f35", size = 241094, upload-time = "2026-04-25T11:05:33.651Z" }, + { url = "https://files.pythonhosted.org/packages/d8/17/92e33338db8c18add33a46b56c2b7d5dcc6cc2ac076c45389f6017b1bf37/xxhash-3.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:11dd69b1a34b7b9af29012f390825b0cdb0617c0966560e227ca74daa7478ba9", size = 197721, upload-time = "2026-04-25T11:05:35.387Z" }, + { url = "https://files.pythonhosted.org/packages/c7/04/fd4114a0820913f336bef5c82ef851bde8d06270982ebd7b2a859961bbf2/xxhash-3.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:01cf5c5333aed26cc8d5eea33b8d6398e085e365a704b7372fabdf7ab06441a9", size = 210073, upload-time = "2026-04-25T11:05:37.405Z" }, + { url = "https://files.pythonhosted.org/packages/dd/eb/a2472b8b81cd576a9af3a4889ad8ba5784e8c5a04592587056cdaededd6c/xxhash-3.7.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f1e65d52c2d526734abecb98372c256b7eacce8fdc42e0df8570417fb39e2772", size = 274960, upload-time = "2026-04-25T11:05:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d3/493afc544aae50b5fb2844ceaeb3697283bb59695db1a7cb40448636de05/xxhash-3.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8ff00fcc3eb436617ed8556cf15daf76c2b501248361a065625a588af78a0a02", size = 413113, upload-time = "2026-04-25T11:05:40.669Z" }, + { url = "https://files.pythonhosted.org/packages/50/6a/002800845a22bff32bcf5fd09caceb4d3f5c3da6b754c46edb9743ce908b/xxhash-3.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b5cd29840505631c6f7dbb8a5d34b742b5e6bbda38fe0b9f54e825f3ea6b61dc", size = 190677, upload-time = "2026-04-25T11:05:42.403Z" }, + { url = "https://files.pythonhosted.org/packages/f4/0f/86ee514622a381c0dc49167c8d431a22aa93518a4063559c3e36e4b82bc8/xxhash-3.7.0-cp310-cp310-win32.whl", hash = "sha256:5bf2f1940499839b39fef1561b5ecb6ede9ac34ef4457474e1337fc7ef07c2f3", size = 30627, upload-time = "2026-04-25T11:05:44.022Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/2ef2310803efb4a2d07844e8098d797e25702024793aa2e85858623a43b5/xxhash-3.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:d41fcda2fa8ca682ebca134a2f2dc02575ba549267585597e73061565795f475", size = 31463, upload-time = "2026-04-25T11:05:45.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/75/40dbf8f142baf8993c38cd988c8d8f51fe0c51e6c84c5769a3c0280a651d/xxhash-3.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:a845a59664d5c531525a467470220f8edc37959e0a6f8e734ffb6654da5c4bee", size = 27747, upload-time = "2026-04-25T11:05:46.422Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f4/7bd35089ff1f8e2c96baa2dce05775a122aacd2e3830a73165e27a4d0848/xxhash-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdc7d06929ae28dda98297a18eef7b0fd38991a3b405d8d7b55c9ef24c296958", size = 33423, upload-time = "2026-04-25T11:05:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/a3/26/4e00c88a6a2c8a759cfb77d2a9a405f901e8aa66e60ef1fd0aeb35edda48/xxhash-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea6daa712f4e094a30830cf01e9b47d03b24d05cc9dab8609f0d9a9db8454712", size = 30857, upload-time = "2026-04-25T11:05:49.189Z" }, + { url = "https://files.pythonhosted.org/packages/82/2f/eeb942c17a5a761a8f01cb9180a0b76bfb62a2c39e6f46b1f9001899027a/xxhash-3.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9e6c0d843f1daf85ea23aeb053579135552bde575b7b98af20bfc667b6e4548d", size = 194702, upload-time = "2026-04-25T11:05:50.457Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/96f132c08b1e5951c68691d3b9ec351ec2edc028f6a01fcd294f46b9d9f0/xxhash-3.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:363c139bf15e1ac5f136b981d3c077eb551299b1effede7f12faa010b8590a60", size = 213613, upload-time = "2026-04-25T11:05:52.571Z" }, + { url = "https://files.pythonhosted.org/packages/82/89/d4e92b796c5ed052d29ed324dbfc1dc1188e0c4bf64bebbf0f8fc20698df/xxhash-3.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a778b25874cb0f862eaab5986bff4ca49ffb0def7c0a34c237b948b3c6c775b2", size = 236726, upload-time = "2026-04-25T11:05:54.395Z" }, + { url = "https://files.pythonhosted.org/packages/40/f1/81fc4361921dc6e557a9c60cb3712f36d244d06eeeb71cd2f4252ac42678/xxhash-3.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e1860f1e43d40e9d904cf22d93e587ea42e010ebce4160877e46bcab4bc232a", size = 212443, upload-time = "2026-04-25T11:05:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d0/afeddd4cff50a332f50d4b8a2e8857673153ab0564ef472fcdeb0b5430df/xxhash-3.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9122ad6f867c4a0f5e655f5c3bdf89103852009dbb442a3d23e688b9e699e800", size = 445793, upload-time = "2026-04-25T11:05:58.953Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d0/3c91e4e6a05ca4d7df8e39ec3a75b713609258ec84705ab34be6430826a1/xxhash-3.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d9110d0c3fb02679972837a033251fd186c529aa62f19c132fc909c74052b8", size = 193937, upload-time = "2026-04-25T11:06:00.546Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3a/a6b0772d9801dd4bea4ca4fd34734d6e9b51a711c8a611a24a79de26a878/xxhash-3.7.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:347a93f2b4ce67ce61959665e32a7447c380f8347e55e100daa23766baacf0e5", size = 285188, upload-time = "2026-04-25T11:06:01.96Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/cf8e31fd7282230fe7367cd501a2e75b4b67b222bfc7eacccfc20d2652cb/xxhash-3.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acbb48679ddf3852c45280c10ff10d52ca2cd1da2e552fb81db1ff786c75d0e4", size = 210966, upload-time = "2026-04-25T11:06:03.453Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f0/fd36cc4a81bf52ee5633275daae2b93dd958aace67fd4f5d466ec83b5f35/xxhash-3.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fe14c356f8b23ad811dc026077a6d4abccdaa7bce5ca98579605550657b6fcfb", size = 241994, upload-time = "2026-04-25T11:06:05.264Z" }, + { url = "https://files.pythonhosted.org/packages/08/e1/67f5d9c9369be42eaf99ba02c01bf14c5ecd67087b02567960bfcee43b63/xxhash-3.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f420ad3d41e38194353a498bbc9561fd5a9973a27b536ce46d8583479cf44335", size = 198707, upload-time = "2026-04-25T11:06:07.044Z" }, + { url = "https://files.pythonhosted.org/packages/50/17/a4c865ca22d2da6b1bc7d739bf88cab209533cf52ba06ca9da27c3039bee/xxhash-3.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:693d02c6dc7d1aa0a45921d54cd8c1ff629e09dfdc2238471507af1f7a1c6f04", size = 210917, upload-time = "2026-04-25T11:06:08.853Z" }, + { url = "https://files.pythonhosted.org/packages/49/8b/453b35810d697abac3c96bde3528bece685869227da274eb80a4a4d4a119/xxhash-3.7.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:14bf7a54e43825ec131ee7fe3c60e142e7c2c1e676ad0f93fc893432d15414af", size = 275772, upload-time = "2026-04-25T11:06:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ad/4eed7eab07fd3ee6678f416190f0413d097ab5d7c1278906bf1e9549d789/xxhash-3.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ae3a39a4d96bdb6f8d154fd7f490c4ad06f0532fcd2bb656052a9a7762cf5d31", size = 414068, upload-time = "2026-04-25T11:06:12.511Z" }, + { url = "https://files.pythonhosted.org/packages/d3/4e/fd6f8a680ba248fdb83054fa71a8bfa3891225200de1708b888ef2c49829/xxhash-3.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1cc07c639e3a77ef1d32987464d3e408565b8a3be57b545d3542b191054d9923", size = 191459, upload-time = "2026-04-25T11:06:14.07Z" }, + { url = "https://files.pythonhosted.org/packages/50/7c/8cb34b3bed4f44ca6827a534d50833f9bc6c006e83b0eb410ac9fa0793bd/xxhash-3.7.0-cp311-cp311-win32.whl", hash = "sha256:3281ba1d1e60ee7a382a7b958513ba03c2c0d5fcbd9a6f7517c0a81251a23422", size = 30628, upload-time = "2026-04-25T11:06:15.802Z" }, + { url = "https://files.pythonhosted.org/packages/0b/47/a49767bd7b40782bedae9ff0721bfe1d7e4dd9dc1585dea684e57ba67c20/xxhash-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7f25baec4c5d851d40718d6fae52285b31683093d4ff5207e63ab306ccf14a5", size = 31461, upload-time = "2026-04-25T11:06:17.104Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c6/3957bfacfb706bd687be246dfa8dd60f8df97c44186d229f7fd6e26c4b7e/xxhash-3.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:4c2454448ce847c72635827bb75c15c5a3434b03ee1afd28cb6dc6fb2597d830", size = 27746, upload-time = "2026-04-25T11:06:18.716Z" }, + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644, upload-time = "2026-04-25T11:08:00.658Z" }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003, upload-time = "2026-04-25T11:08:02.338Z" }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655, upload-time = "2026-04-25T11:08:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664, upload-time = "2026-04-25T11:08:05.011Z" }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317, upload-time = "2026-04-25T11:08:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457, upload-time = "2026-04-25T11:08:08.099Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894, upload-time = "2026-04-25T11:08:09.429Z" }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409, upload-time = "2026-04-25T11:08:10.91Z" }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135, upload-time = "2026-04-25T11:08:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379, upload-time = "2026-04-25T11:08:14.206Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447, upload-time = "2026-04-25T11:08:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660, upload-time = "2026-04-25T11:08:17.441Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076, upload-time = "2026-04-25T11:08:19.134Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990, upload-time = "2026-04-25T11:08:20.618Z" }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590, upload-time = "2026-04-25T11:08:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442, upload-time = "2026-04-25T11:08:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356, upload-time = "2026-04-25T11:08:25.99Z" }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898, upload-time = "2026-04-25T11:08:27.608Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519, upload-time = "2026-04-25T11:08:29.301Z" }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191, upload-time = "2026-04-25T11:08:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604, upload-time = "2026-04-25T11:08:32.862Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271, upload-time = "2026-04-25T11:08:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284, upload-time = "2026-04-25T11:08:35.987Z" }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701, upload-time = "2026-04-25T11:08:37.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646, upload-time = "2026-04-25T11:08:39.109Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125, upload-time = "2026-04-25T11:08:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633, upload-time = "2026-04-25T11:08:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899, upload-time = "2026-04-25T11:08:43.645Z" }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116, upload-time = "2026-04-25T11:08:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012, upload-time = "2026-04-25T11:08:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534, upload-time = "2026-04-25T11:08:49.149Z" }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217, upload-time = "2026-04-25T11:08:50.805Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906, upload-time = "2026-04-25T11:08:52.418Z" }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057, upload-time = "2026-04-25T11:08:54.105Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886, upload-time = "2026-04-25T11:08:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015, upload-time = "2026-04-25T11:08:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457, upload-time = "2026-04-25T11:08:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738, upload-time = "2026-04-25T11:09:01.423Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127, upload-time = "2026-04-25T11:09:03.592Z" }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962, upload-time = "2026-04-25T11:09:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643, upload-time = "2026-04-25T11:09:08.153Z" }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522, upload-time = "2026-04-25T11:09:09.534Z" }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807, upload-time = "2026-04-25T11:09:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/54/c1/e57ac7317b1f58a92bab692da6d497e2a7ce44735b224e296347a7ecc754/xxhash-3.7.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad3aa71e12ee634f22b39a0ff439357583706e50765f17f05550f92dbf128a23", size = 31232, upload-time = "2026-04-25T11:10:21.51Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4e/075559bd712bc62e84915ea46bbee859f935d285659082c129bdbff679dd/xxhash-3.7.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5de686e73690cdaf72b96d4fa083c230ec9020bcc2627ce6316138e2cf2fe2d1", size = 28553, upload-time = "2026-04-25T11:10:23.1Z" }, + { url = "https://files.pythonhosted.org/packages/92/ca/a9c78cb384d4b033b0c58196bd5c8509873cabe76389e195127b0302a741/xxhash-3.7.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7fbec49f5341bbdea0c471f7d1e2fb41ae8925af9b6f28025c28defd8eb94274", size = 41109, upload-time = "2026-04-25T11:10:25.022Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b1/dfe2629f7c77eb2fa234c72ff537cdd64939763df704e256446ed364a16d/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b542c347c2089f43dc5a6db31d2a6f3cdb04ee33505ec6e9f653834dbb0bde", size = 36307, upload-time = "2026-04-25T11:10:26.949Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f7/5a484afce0f48dd8083208b42e4911f290a82c7b52458ef2927e4d421a45/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a169a036bed0995e090d1493b283cc2cc8a6f5046821086b843abefff80643bc", size = 32534, upload-time = "2026-04-25T11:10:29.01Z" }, + { url = "https://files.pythonhosted.org/packages/0f/5f/4acfcd490db9780cf36c58534d828003c564cde5350220a1c783c4d10776/xxhash-3.7.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ec101643395d7f21405b640f728f6f627e6986557027d740f2f9b220955edafe", size = 31552, upload-time = "2026-04-25T11:10:30.727Z" }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" }, + { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" }, + { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" }, + { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" }, + { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" }, + { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" }, + { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" }, + { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" }, + { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" }, + { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" }, + { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" }, + { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, + { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, + { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, + { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, + { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, + { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, + { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, + { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, + { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, + { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, + { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, + { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, + { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, + { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, + { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, + { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, + { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, + { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, + { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, + { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, + { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, + { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, + { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, + { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, + { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, + { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, +] From d5560819b3a89c4d09070b3ed7fe7d2378799dda Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 17 May 2026 22:32:59 +0530 Subject: [PATCH 37/79] fix: bg threading for async control plane --- examples/mini_swe_env/train_swe_async_grpo.py | 66 +++++++++++++++++-- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 223c2e0ae..6d2598563 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -8,6 +8,10 @@ vLLM runs on a separate GPU. The trainer, interception server, and rollout worker share a process on the training GPU. +The InterceptionServer runs in a background thread with its own asyncio +event loop so it stays alive while the synchronous trainer.train() runs +on the main thread. + Prerequisites: CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-1.7B \\ --tensor-parallel-size 1 --max-model-len 4096 @@ -27,7 +31,9 @@ import logging import os import sys +import threading from pathlib import Path +from typing import Any _root = Path(__file__).resolve().parent.parent.parent for _p in (_root / "src", _root / "envs"): @@ -54,6 +60,54 @@ _log = logging.getLogger("swe-async-grpo") +# ── InterceptionServer background thread ────────────────────────── + + +def _run_event_loop(loop: asyncio.AbstractEventLoop) -> None: + """Run an asyncio event loop forever in the current thread.""" + asyncio.set_event_loop(loop) + loop.run_forever() + + +def start_interception_server( + control_plane: SWEAsyncControlPlane, +) -> tuple[asyncio.AbstractEventLoop, threading.Thread]: + """Start the InterceptionServer in a daemon thread. + + Returns the event loop and the thread so the caller can shut it down. + The aiohttp server runs on this loop and stays alive as long as the + thread is running. + """ + loop = asyncio.new_event_loop() + # Start the server on the new loop. + future = asyncio.run_coroutine_threadsafe(control_plane.start(), loop) + # The loop must be running for the coroutine to execute. + thread = threading.Thread(target=_run_event_loop, args=(loop,), daemon=True, + name="interception-server") + thread.start() + # Wait for start() to complete. + future.result(timeout=30) + return loop, thread + + +def stop_interception_server( + control_plane: SWEAsyncControlPlane, + loop: asyncio.AbstractEventLoop, + thread: threading.Thread, +) -> None: + """Shut down the InterceptionServer and its event loop.""" + future = asyncio.run_coroutine_threadsafe(control_plane.stop(), loop) + try: + future.result(timeout=10) + except Exception: + pass + loop.call_soon_threadsafe(loop.stop) + thread.join(timeout=5) + + +# ── CLI ──────────────────────────────────────────────────────────── + + def _args() -> argparse.Namespace: p = argparse.ArgumentParser() p.add_argument("--task-variant", default="lite", choices=["lite", "full"]) @@ -103,10 +157,11 @@ def main() -> int: if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token - # ── Interception control plane ──────────────────────────────── + # ── Interception control plane (background thread) ──────────── control_cfg = SWEAsyncControlPlaneConfig.from_env() control_plane = SWEAsyncControlPlane(config=control_cfg) - asyncio.run(control_plane.start()) + server_loop, server_thread = start_interception_server(control_plane) + _log.info("InterceptionServer running in background thread") try: # ── Session factory (Pi in sandbox) ─────────────────────── @@ -140,7 +195,7 @@ def main() -> int: ) # ── Trainer ─────────────────────────────────────────────── - def _noop_reward(**kwargs: Any) -> list[float]: # noqa: ANN401 + def _noop_reward(**kwargs: Any) -> list[float]: """Unused — rewards come from rollout_worker.advantage.""" prompts = kwargs.get("prompts", []) return [0.0] * len(prompts) @@ -152,7 +207,7 @@ def _noop_reward(**kwargs: Any) -> list[float]: # noqa: ANN401 processing_class=tokenizer, rollout_worker=worker, args=AsyncGRPOConfig( - output_dir="outputs/swe_async_grpo", + output_dir=os.path.join(os.environ.get("HOME", "/tmp"), "outputs/swe_async_grpo"), vllm_server_base_url=vllm_url, vllm_server_timeout=2400.0, max_completion_length=2048, @@ -177,9 +232,8 @@ def _noop_reward(**kwargs: Any) -> list[float]: # noqa: ANN401 _log.info("done: step=%s", getattr(trainer.state, "global_step", "?")) return 0 finally: - asyncio.run(control_plane.stop()) + stop_interception_server(control_plane, server_loop, server_thread) if __name__ == "__main__": - from typing import Any # noqa: E402 raise SystemExit(main()) From 448f6905f258aa1ca0032812b4cb454bd31380ea Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 13:18:09 +0530 Subject: [PATCH 38/79] fix: thread-safe queue handling --- .../harness/agents/interception_server.py | 96 +++++++++++++++++-- .../core/harness/sandbox/hf_backend.py | 5 +- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index 70f8c4247..7bc67fedc 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -164,6 +164,12 @@ def register_rollout( "tool_handlers": {}, "tool_defs": {}, } + active = len(self.active_rollouts) + _log.info( + "interception_rollout_registered rollout_id=%s active_rollouts=%d", + rollout_id, + active, + ) return queue def unregister_rollout(self, rollout_id: str) -> None: @@ -176,7 +182,9 @@ def unregister_rollout(self, rollout_id: str) -> None: matching_intercepts = [self.intercepts[i] for i in matching_ids] for request_id in matching_ids: del self.intercepts[request_id] - self.active_rollouts.pop(rollout_id, None) + removed = self.active_rollouts.pop(rollout_id, None) is not None + active = len(self.active_rollouts) + pending = len(self.intercepts) for intercept in matching_intercepts: fut: asyncio.Future | None = intercept.get("response_future") @@ -189,10 +197,27 @@ def unregister_rollout(self, rollout_id: str) -> None: except asyncio.QueueFull: pass + _log.info( + "interception_rollout_unregistered rollout_id=%s removed=%s " + "active_rollouts=%d pending_intercepts=%d", + rollout_id, + removed, + active, + pending, + ) + def get_intercept(self, request_id: str) -> dict[str, Any] | None: with self._state_lock: return self.intercepts.get(request_id) + def stats(self) -> dict[str, int]: + """Return lightweight runtime counters for health/debug views.""" + with self._state_lock: + return { + "active_rollouts": len(self.active_rollouts), + "pending_intercepts": len(self.intercepts), + } + def register_tool_handler( self, rollout_id: str, @@ -269,7 +294,7 @@ def _authorized(self, request: web.Request) -> bool: ) or hmac.compare_digest(api_key, self.secret) async def _handle_health(self, request: web.Request) -> web.Response: - return web.json_response({"status": "ok"}) + return web.json_response({"status": "ok", **self.stats()}) async def _handle_tool_call(self, request: web.Request) -> web.Response: if not self._authorized(request): @@ -433,6 +458,53 @@ async def _stream_response( return resp +def _resolve_future_threadsafe( + future: asyncio.Future, value: Any +) -> None: + """Set a future's result from any thread. + + ``asyncio.Future`` is not thread-safe: calling ``set_result`` from a + thread that is not running the future's event loop can silently fail + to wake the coroutine awaiting it. This helper detects cross-loop + calls and uses ``call_soon_threadsafe`` to schedule the resolution on + the correct loop. + """ + if future.done(): + return + loop = future.get_loop() + try: + running = asyncio.get_running_loop() + except RuntimeError: + running = None + if running is loop: + future.set_result(value) + else: + loop.call_soon_threadsafe(future.set_result, value) + + +def _put_queue_threadsafe( + q: asyncio.Queue, item: Any +) -> None: + """Put an item on an asyncio.Queue from any thread.""" + loop = getattr(q, "_loop", None) + if loop is None: + # Fallback: try put_nowait which is simpler. + try: + q.put_nowait(item) + return + except asyncio.QueueFull: + pass + return + try: + running = asyncio.get_running_loop() + except RuntimeError: + running = None + if running is loop: + q.put_nowait(item) + else: + loop.call_soon_threadsafe(q.put_nowait, item) + + async def deliver_response( intercept: dict[str, Any], response_dict: dict[str, Any] ) -> None: @@ -441,14 +513,20 @@ async def deliver_response( For non-streaming requests, resolves the future directly. For streaming requests, synthesizes SSE chunks from the complete response and signals EOF. + + Thread-safe: can be called from any thread, not just the event loop + that owns the future/queue. This is required because the rollout + worker may run ``deliver_response`` from its own ``asyncio.run()`` + in a daemon thread while the ``InterceptionServer``'s aiohttp + handler awaits the future on a different loop. """ is_streaming = intercept.get("stream", False) chunk_queue: asyncio.Queue | None = intercept.get("chunk_queue") future: asyncio.Future | None = intercept.get("response_future") if not is_streaming: - if future and not future.done(): - future.set_result(response_dict) + if future: + _resolve_future_threadsafe(future, response_dict) return if chunk_queue is None: @@ -474,7 +552,7 @@ async def deliver_response( } ], } - await chunk_queue.put(content_chunk) + _put_queue_threadsafe(chunk_queue, content_chunk) finish_chunk = { "id": response_dict.get("id", ""), "object": "chat.completion.chunk", @@ -488,11 +566,11 @@ async def deliver_response( } ], } - await chunk_queue.put(finish_chunk) + _put_queue_threadsafe(chunk_queue, finish_chunk) - await chunk_queue.put(None) - if future and not future.done(): - future.set_result(response_dict) + _put_queue_threadsafe(chunk_queue, None) + if future: + _resolve_future_threadsafe(future, response_dict) __all__ = [ diff --git a/src/openenv/core/harness/sandbox/hf_backend.py b/src/openenv/core/harness/sandbox/hf_backend.py index 3857ea7e6..3b7b060b5 100644 --- a/src/openenv/core/harness/sandbox/hf_backend.py +++ b/src/openenv/core/harness/sandbox/hf_backend.py @@ -231,15 +231,16 @@ def create( image: str | None = None, ) -> SandboxHandle: # `hf-sandbox` does not support metadata at create-time yet. - del metadata, image + del metadata timeout = self._timeout or _format_timeout(timeout_s) + effective_image = image or self._image last_error: Exception | None = None for attempt in range(self._create_retries): try: sbx = Sandbox.create( - image=self._image, + image=effective_image, flavor=self._flavor, timeout=timeout, forward_hf_token=self._forward_hf_token, From 37e549d5dc52fa318b88aae16f2edc0a2c68e805 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 14:18:52 +0530 Subject: [PATCH 39/79] fix: interception gate support in CodingAgentSessionFactory --- envs/coding_agent_env/harness.py | 33 +++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/envs/coding_agent_env/harness.py b/envs/coding_agent_env/harness.py index 2355260f5..748dcb091 100644 --- a/envs/coding_agent_env/harness.py +++ b/envs/coding_agent_env/harness.py @@ -8,6 +8,8 @@ from __future__ import annotations +import asyncio +import uuid from typing import Any, Literal from openenv.core.harness import ResourceSessionFactory @@ -21,11 +23,7 @@ from openenv.core.harness.sandbox import SandboxBackend, SandboxHandle from .config import CodingAgentConfig -from .opencode_runtime import ( - agent_log_path, - build_env_vars, - build_run_cmd, -) +from .opencode_runtime import agent_log_path, build_env_vars, build_run_cmd from .task import CodingAgentTask @@ -124,12 +122,37 @@ def create( _log.error("factory.create: bootstrap failed: %r", exc) sandbox.kill() raise + + # Wire up interception_gate if the driver is configured for it + base_url_override: str | None = None + interception_rollout_id: str | None = None + interception_queue: asyncio.Queue | None = None + + if self._driver.mode == "interception_gate": + assert self._driver._interception_server is not None + assert self._driver._interception_base_url is not None + rollout_id = episode_id or f"rollout_{uuid.uuid4().hex[:8]}" + interception_rollout_id = rollout_id + interception_queue = self._driver._interception_server.register_rollout( + rollout_id + ) + base_url_override = ( + f"{self._driver._interception_base_url.rstrip('/')}" + f"/rollout/{rollout_id}/v1" + ) + session = CodingAgentSession( sandbox=sandbox, config=self._config, task=oc_task, verifier=self._verifier, + base_url_override=base_url_override, ) + # Pass interception fields to the parent CLIAgentSession + session._interception_server = self._driver._interception_server + session._interception_rollout_id = interception_rollout_id + session._interception_queue = interception_queue + session.start_agent() return session From 8aa9d18ce39a1e3d7a97d2ddde5c0572d60b3e8c Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 14:28:35 +0530 Subject: [PATCH 40/79] fix: improve error handling and config propagation across agent pipeline - Wire disable_thinking and max_tokens_cap through CodingAgentConfig - Raise RuntimeError on mkdir/cat failures in docker backend - Propagate QueueFull exceptions instead of silently swallowing - Change CommandResult.exit_code to int | None for bootstrap clarity --- envs/coding_agent_env/config.py | 4 ++++ envs/coding_agent_env/models.py | 9 ++++++-- envs/coding_agent_env/opencode_runtime.py | 15 +++++++++++-- .../server/coding_environment.py | 21 +++++++++++++++---- .../harness/agents/interception_server.py | 17 +++++---------- .../core/harness/sandbox/docker_backend.py | 14 +++++++++++-- 6 files changed, 58 insertions(+), 22 deletions(-) diff --git a/envs/coding_agent_env/config.py b/envs/coding_agent_env/config.py index b3243253e..d70610542 100644 --- a/envs/coding_agent_env/config.py +++ b/envs/coding_agent_env/config.py @@ -45,6 +45,10 @@ class CodingAgentConfig(BaseModel): extra_env: dict[str, str] = Field(default_factory=dict) extra_setup_shell: str | None = None + # --- Model behavior -------------------------------------------------------- + disable_thinking: bool = False + max_tokens_cap: int | None = None + # --- Sandbox paths -------------------------------------------------------- # Root directory inside the sandbox where the primitive writes config, # task files, and logs. E2B's default user is ``user`` with home diff --git a/envs/coding_agent_env/models.py b/envs/coding_agent_env/models.py index 2111d84d5..e338a4867 100644 --- a/envs/coding_agent_env/models.py +++ b/envs/coding_agent_env/models.py @@ -35,10 +35,15 @@ class RolloutTurn(BaseModel): class CommandResult(BaseModel): - """Outcome of one bash command in setup/verify.""" + """Outcome of one bash command in setup/verify. + + When ``exit_code`` is ``None``, the command ran during sandbox bootstrap + and its individual exit code was not captured (bootstrap succeeds or fails + atomically). + """ cmd: str - exit_code: int + exit_code: int | None = None stdout: str = "" stderr: str = "" duration_s: float = 0.0 diff --git a/envs/coding_agent_env/opencode_runtime.py b/envs/coding_agent_env/opencode_runtime.py index 49855528b..31285556e 100644 --- a/envs/coding_agent_env/opencode_runtime.py +++ b/envs/coding_agent_env/opencode_runtime.py @@ -52,6 +52,12 @@ def build_opencode_json(config: CodingAgentConfig) -> str: """ provider_name = "intercepted" + model_key = config.model.split("/", 1)[-1] + + model_block: dict[str, Any] = {"name": "Intercepted Model"} + if config.max_tokens_cap is not None: + model_block["limit"] = {"output": config.max_tokens_cap} + provider_block: dict[str, Any] = { "npm": provider_npm_package(config.provider), "name": "Intercepted", @@ -61,16 +67,21 @@ def build_opencode_json(config: CodingAgentConfig) -> str: "timeout": config.request_timeout_ms, }, "models": { - config.model.split("/", 1)[-1]: {"name": "Intercepted Model"}, + model_key: model_block, }, } doc: dict[str, Any] = { "$schema": "https://opencode.ai/config.json", - "model": f"{provider_name}/{config.model.split('/', 1)[-1]}", + "model": f"{provider_name}/{model_key}", "provider": {provider_name: provider_block}, } + # Disable thinking/reasoning tokens when requested. AI SDK respects + # the top-level "reasoning" key to control reasoning token generation. + if config.disable_thinking: + doc["reasoning"] = "none" + tools = _build_tools_block(config) if tools: doc["tools"] = tools diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index b1e7f47ef..9000ed4e0 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -22,6 +22,7 @@ from __future__ import annotations import json +import logging import os import time from typing import Any, Optional @@ -50,6 +51,8 @@ HOME = "/home/user" WORKDIR = f"{HOME}/workdir" INSTRUCTION_PATH = f"{HOME}/task/instruction.md" +_log = logging.getLogger(__name__) + REWARD_FILE = f"{HOME}/logs/verifier/reward.txt" PROXY_LOG = f"{HOME}/logs/agent/proxy.log" AGENT_LOG = f"{HOME}/logs/agent/opencode.jsonl" @@ -83,21 +86,22 @@ def __init__(self) -> None: # Lazy imports so module import stays cheap and so tests can patch. try: from ..models import ( - CommandResult, CodingAgentState, + CommandResult, RolloutResult, RolloutTurn, ) except ImportError: # pragma: no cover from models import ( # type: ignore - CommandResult, CodingAgentState, + CommandResult, RolloutResult, RolloutTurn, ) from openenv.core.harness.agents import get_agent_spec from openenv.core.harness.agents.cli_driver import CLIAgentSessionFactory + from coding_agent_env.config import CodingAgentConfig from coding_agent_env.harness import CodingAgentSessionFactory from coding_agent_env.task import CodingAgentTask @@ -374,8 +378,8 @@ def _emit(msg: str) -> None: result.setup_results.append( self._CommandResult( cmd=cmd, - exit_code=0, - stdout="executed during bootstrap", + exit_code=None, + stdout="executed during bootstrap (individual exit code not captured)", stderr="", duration_s=0.0, ) @@ -466,12 +470,21 @@ def _build_agent_config( max_tokens_cap: int, ) -> Any: if agent == "opencode": + if top_logprobs: + _log.warning( + "top_logprobs=%d is not supported for agent='opencode' " + "and will have no effect. Use interception_gate mode for " + "logprob capture.", + top_logprobs, + ) return self._CodingAgentConfig( provider="openai_compatible", base_url=base_url.rstrip("/"), api_key=api_key, model=model, agent_timeout_s=agent_timeout_s, + disable_thinking=disable_thinking, + max_tokens_cap=max_tokens_cap if max_tokens_cap != 4096 else None, ) provider = self._infer_pi_provider(base_url) diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index 7bc67fedc..5e541700d 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -458,9 +458,7 @@ async def _stream_response( return resp -def _resolve_future_threadsafe( - future: asyncio.Future, value: Any -) -> None: +def _resolve_future_threadsafe(future: asyncio.Future, value: Any) -> None: """Set a future's result from any thread. ``asyncio.Future`` is not thread-safe: calling ``set_result`` from a @@ -482,18 +480,13 @@ def _resolve_future_threadsafe( loop.call_soon_threadsafe(future.set_result, value) -def _put_queue_threadsafe( - q: asyncio.Queue, item: Any -) -> None: +def _put_queue_threadsafe(q: asyncio.Queue, item: Any) -> None: """Put an item on an asyncio.Queue from any thread.""" loop = getattr(q, "_loop", None) if loop is None: - # Fallback: try put_nowait which is simpler. - try: - q.put_nowait(item) - return - except asyncio.QueueFull: - pass + # Fallback: put_nowait which is simpler. Let QueueFull propagate — + # silently dropping items would cause hard-to-debug streaming issues. + q.put_nowait(item) return try: running = asyncio.get_running_loop() diff --git a/src/openenv/core/harness/sandbox/docker_backend.py b/src/openenv/core/harness/sandbox/docker_backend.py index a64070a46..120fb9a11 100644 --- a/src/openenv/core/harness/sandbox/docker_backend.py +++ b/src/openenv/core/harness/sandbox/docker_backend.py @@ -162,12 +162,17 @@ def start_bg( def write_text(self, path: str, content: str) -> None: parent = str(PurePosixPath(path).parent) if parent not in ("", "/"): - subprocess.run( + mkdir_result = subprocess.run( ["docker", "exec", self._container_id, "mkdir", "-p", parent], capture_output=True, timeout=10, ) - subprocess.run( + if mkdir_result.returncode != 0: + raise RuntimeError( + f"Failed to create directory {parent!r} in container " + f"{self._container_id}: {mkdir_result.stderr.decode(errors='replace')}" + ) + write_result = subprocess.run( [ "docker", "exec", @@ -181,6 +186,11 @@ def write_text(self, path: str, content: str) -> None: capture_output=True, timeout=30, ) + if write_result.returncode != 0: + raise RuntimeError( + f"Failed to write file {path!r} in container " + f"{self._container_id}: {write_result.stderr.decode(errors='replace')}" + ) def read_text(self, path: str) -> str: result = subprocess.run( From 61e5524762182be06d43b290895d33f0e3e66b09 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 14:35:18 +0530 Subject: [PATCH 41/79] fix: whitespace secret validation + conditional /root/ write --- src/openenv/core/harness/agents/cli_driver.py | 5 ++++- src/openenv/core/harness/agents/interception_server.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 2ff07e4d2..587362746 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -529,7 +529,10 @@ def _write_pi_models_config( }, indent=2, ) - for path in {f"{home}/.pi/agent/models.json", "/root/.pi/agent/models.json"}: + paths = {f"{home}/.pi/agent/models.json"} + if home == "/root": + paths.add("/root/.pi/agent/models.json") + for path in paths: sandbox.write_text(path, content) def _resolve_env_vars( diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index 5e541700d..19b05bb95 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -85,6 +85,8 @@ def __init__( self.port = port self.host = host self.secret = secret or secrets.token_urlsafe(32) + if not self.secret.strip(): + raise ValueError("InterceptionServer secret must not be blank.") self._app: web.Application | None = None self._runner: web.AppRunner | None = None self._site: web.TCPSite | None = None From a2b43887c0b207837fb67c1d5d96d8e9c3bdf0fb Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 15:17:50 +0530 Subject: [PATCH 42/79] fix: cross-loop safe request queue via stdlib queue.Queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit replace asyncio.Queue with stdlib queue.Queue for the request notification path (server → training loop). This makes both directions of the InterceptionServer cross-loop/cross-thread safe: - Request notifications: queue.Queue (inherently thread-safe) - Response delivery: asyncio.Future via _resolve_future_threadsafe (already cross-loop safe) The consumer (next_request) uses asyncio.to_thread(q.get, timeout=...) to await without blocking the event loop. This follows the same pattern used by OpenClaw-RL at scale. chunk_queue (internal SSE streaming) remains asyncio.Queue since both producer and consumer run on the server's own event loop. --- src/openenv/core/harness/agents/cli_driver.py | 17 +++++++---------- .../core/harness/agents/interception_server.py | 17 +++++++++-------- tests/core/test_cli_agent_driver.py | 12 ++++++------ tests/core/test_interception_server.py | 6 +++--- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index 587362746..a2724162e 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -20,6 +20,7 @@ import asyncio import json import logging +import queue as _queue_mod import shlex import time import uuid @@ -36,11 +37,7 @@ from openenv.core.harness.sandbox import BgJob, SandboxBackend, SandboxHandle from .base import CLIAgentSpec -from .interception_server import ( - deliver_response, - InterceptionServer, - ToolHandler, -) +from .interception_server import deliver_response, InterceptionServer, ToolHandler _log = logging.getLogger(__name__) @@ -76,7 +73,7 @@ def __init__( agent_bg_job: BgJob | None = None, interception_server: InterceptionServer | None = None, interception_rollout_id: str | None = None, - interception_queue: asyncio.Queue | None = None, + interception_queue: _queue_mod.Queue[str] | None = None, ) -> None: self.spec = spec self.sandbox = sandbox @@ -204,14 +201,14 @@ async def next_request( f"{self.spec.name} interception_gate: no request within timeout" ) try: - request_id = await asyncio.wait_for( - self._interception_queue.get(), + request_id = await asyncio.to_thread( + self._interception_queue.get, timeout=min(remaining, 1.0), ) intercept = server.get_intercept(request_id) if intercept is not None: return intercept - except asyncio.TimeoutError: + except _queue_mod.Empty: pass if self._agent_bg_job is not None: @@ -317,7 +314,7 @@ def create_session( base_url_override: str | None = None interception_rollout_id: str | None = None - interception_queue: asyncio.Queue | None = None + interception_queue: _queue_mod.Queue[str] | None = None if self.mode == "interception_gate": assert self._interception_server is not None diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index 19b05bb95..a71082e69 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -31,11 +31,11 @@ # Docker: base_url = f"http://host.docker.internal:{server.port}" # Remote: base_url = your_tunnel_or_public_url - queue = server.register_rollout(rollout_id) + request_queue = server.register_rollout(rollout_id) # Agent runs with OPENAI_BASE_URL = f"{base_url}/rollout/{rollout_id}/v1" while True: - request_id = await asyncio.wait_for(queue.get(), timeout=...) + request_id = await asyncio.to_thread(request_queue.get, timeout=...) intercept = server.get_intercept(request_id) if intercept is None: continue @@ -52,6 +52,7 @@ import hmac import json import logging +import queue as _queue_mod import secrets import threading import time @@ -157,11 +158,11 @@ def register_rollout( self, rollout_id: str, state: dict[str, Any] | None = None, - ) -> asyncio.Queue: - queue: asyncio.Queue = asyncio.Queue() + ) -> _queue_mod.Queue[str]: + request_queue: _queue_mod.Queue[str] = _queue_mod.Queue() with self._state_lock: self.active_rollouts[rollout_id] = { - "request_id_queue": queue, + "request_id_queue": request_queue, "state": state, "tool_handlers": {}, "tool_defs": {}, @@ -172,7 +173,7 @@ def register_rollout( rollout_id, active, ) - return queue + return request_queue def unregister_rollout(self, rollout_id: str) -> None: with self._state_lock: @@ -393,8 +394,8 @@ async def _handle_chat_completions( if context is None: return web.json_response({"error": "rollout not found"}, status=404) self.intercepts[request_id] = intercept - request_queue: asyncio.Queue = context["request_id_queue"] - await request_queue.put(request_id) + request_queue: _queue_mod.Queue[str] = context["request_id_queue"] + request_queue.put_nowait(request_id) if is_streaming: return await self._stream_response(request, intercept) diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index 977bf6703..18854fe7e 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -18,6 +18,7 @@ from __future__ import annotations import json +import queue as _queue_mod from dataclasses import dataclass, field from typing import Any @@ -585,7 +586,8 @@ def test_pi_interception_gate_writes_models_json_and_uses_openenv_provider(self) home_models = "/home/user/.pi/agent/models.json" root_models = "/root/.pi/agent/models.json" assert home_models in sbx.written - assert root_models in sbx.written + # /root/ path is only written when sandbox_home == "/root" + assert root_models not in sbx.written cfg = json.loads(sbx.written[home_models]) provider = cfg["providers"]["openenv"] @@ -786,15 +788,13 @@ def test_close_kills_sandbox_and_jobs(self): @pytest.mark.asyncio async def test_next_request_handles_missing_intercept_without_keyerror(self): - import asyncio - from openenv.core.harness.agents.cli_driver import CLIAgentSession from openenv.core.harness.agents.interception_server import InterceptionServer spec = _make_test_spec() sbx = FakeSandbox() - queue: asyncio.Queue[str] = asyncio.Queue() - await queue.put("req_missing") + q: _queue_mod.Queue[str] = _queue_mod.Queue() + q.put("req_missing") session = CLIAgentSession( spec=spec, @@ -804,7 +804,7 @@ async def test_next_request_handles_missing_intercept_without_keyerror(self): agent_bg_job=FakeBgJob(), interception_server=InterceptionServer(secret="s"), interception_rollout_id="rollout-1", - interception_queue=queue, + interception_queue=q, ) # Missing request IDs can happen if unregister_rollout races with queue.get(). diff --git a/tests/core/test_interception_server.py b/tests/core/test_interception_server.py index 77d844aff..73421e1a7 100644 --- a/tests/core/test_interception_server.py +++ b/tests/core/test_interception_server.py @@ -81,7 +81,7 @@ async def test_interception_server_non_stream_roundtrip_cleans_intercept() -> No }, ) ) - request_id = await asyncio.wait_for(queue.get(), timeout=1.0) + request_id = await asyncio.to_thread(queue.get, timeout=1.0) intercept = server.get_intercept(request_id) assert intercept is not None @@ -129,7 +129,7 @@ async def test_interception_server_unregister_rollout_cancels_pending_request() }, ) ) - _request_id = await asyncio.wait_for(queue.get(), timeout=1.0) + _request_id = await asyncio.to_thread(queue.get, timeout=1.0) server.unregister_rollout("r1") resp = await request_task @@ -216,7 +216,7 @@ async def _handler(arguments: dict) -> dict: }, ) ) - request_id = await asyncio.wait_for(queue.get(), timeout=1.0) + request_id = await asyncio.to_thread(queue.get, timeout=1.0) intercept = server.get_intercept(request_id) assert intercept is not None tool_names = { From 4bcb2fda432a91bc2df599099d802aed70245abd Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 16:03:56 +0530 Subject: [PATCH 43/79] feat: NCCL weight transfer and response parsing --- .../mini_swe_env/async_grpo/rollout_worker.py | 427 +++++++++++++++++- 1 file changed, 402 insertions(+), 25 deletions(-) diff --git a/envs/mini_swe_env/async_grpo/rollout_worker.py b/envs/mini_swe_env/async_grpo/rollout_worker.py index ba50bfe30..1a0663d05 100644 --- a/envs/mini_swe_env/async_grpo/rollout_worker.py +++ b/envs/mini_swe_env/async_grpo/rollout_worker.py @@ -25,6 +25,7 @@ from __future__ import annotations import asyncio +import json import logging import queue import threading @@ -35,6 +36,24 @@ import requests +try: + from trl.chat_template_utils import add_response_schema, parse_response +except Exception: # pragma: no cover - defensive for older TRL versions + add_response_schema = None + parse_response = None + +try: + from vllm.distributed.weight_transfer.nccl_engine import ( + NCCLTrainerSendWeightsArgs, + NCCLWeightTransferEngine, + ) + from vllm.utils.network_utils import get_ip, get_open_port +except Exception: # pragma: no cover - optional at import time + NCCLTrainerSendWeightsArgs = None + NCCLWeightTransferEngine = None + get_ip = None + get_open_port = None + from ..models import SWETask _log = logging.getLogger(__name__) @@ -42,6 +61,7 @@ # ── Sample dataclass ─────────────────────────────────────────────────── + @dataclass class RolloutSample: """Matches the fields TRL's ``RolloutQueueDataset`` reads.""" @@ -56,6 +76,7 @@ class RolloutSample: # ── Config ───────────────────────────────────────────────────────────── + @dataclass(frozen=True) class WorkerConfig: max_inflight: int = 2 @@ -64,11 +85,18 @@ class WorkerConfig: max_turns: int = 50 max_completion_tokens: int = 2048 temperature: float = 1.0 + # After returning a terminal plain-text response (finish_reason=stop, + # no tool_calls), wait briefly for a follow-up request before treating + # the rollout as complete. This avoids 600s stalls when agent exit + # detection is delayed on remote backends. + post_response_grace_s: float = 10.0 + stop_on_idle_terminal_response: bool = True idle_backoff_s: float = 0.5 # ── Worker ───────────────────────────────────────────────────────────── + class SWERolloutWorker: """Background rollout producer for Pi + InterceptionServer + vLLM. @@ -90,6 +118,11 @@ def __init__( self._factory = session_factory self._tasks = list(tasks) self._tokenizer = tokenizer + if add_response_schema is not None: + try: + self._tokenizer = add_response_schema(self._tokenizer) + except Exception as exc: + _log.debug("add_response_schema unavailable for tokenizer: %s", exc) self._vllm_base_url = vllm_base_url.rstrip("/") self._vllm_api_key = vllm_api_key self._vllm_model = vllm_model @@ -102,10 +135,14 @@ def __init__( self._stop = threading.Event() self._pause = threading.Event() self._lock = threading.Lock() + self._weight_sync_lock = threading.Lock() self._threads: list[threading.Thread] = [] self._task_idx = 0 self._model_version = 0 self._started = False + self._model_update_group: Any | None = None + + self._init_weight_transfer() # ── RolloutWorkerProtocol ────────────────────────────────────── @@ -117,7 +154,9 @@ def start(self) -> None: self._stop.clear() for i in range(max(1, self._cfg.max_inflight)): t = threading.Thread( - target=self._loop, args=(i,), daemon=True, + target=self._loop, + args=(i,), + daemon=True, name=f"swe-rollout-{i}", ) t.start() @@ -131,22 +170,187 @@ def stop(self) -> None: with self._lock: self._started = False self._threads = [] + self._destroy_model_update_group() def pause(self) -> None: self._pause.set() + if self._model_update_group is None: + return + self._post_json( + "/pause", + timeout=60, + params={"mode": "keep"}, + ) def resume(self) -> None: + if self._model_update_group is not None: + self._post_json("/resume", timeout=60) self._pause.clear() def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: - # Consume iterator (required by protocol). Real NCCL sync is future work. - for _ in iterator: - pass + # Materialize once so we can derive metadata and send the same + # tensors through NCCL. + items = list(iterator) + if not items: + return + + if self._model_update_group is None: + _log.warning( + "weight sync disabled: NCCL weight-transfer group not initialized" + ) + return + + names = [name for name, _ in items] + dtype_names = [ + str(getattr(tensor, "dtype", "float32")).split(".")[-1] + for _, tensor in items + ] + shapes = [list(getattr(tensor, "shape", [])) for _, tensor in items] + update_info = { + "names": names, + "dtype_names": dtype_names, + "shapes": shapes, + "packed": True, + "is_checkpoint_format": True, + } + + with self._weight_sync_lock: + post_error: list[Exception] = [] + + def _post_update() -> None: + try: + self._post_json( + "/update_weights", + timeout=1800, + json_body={"update_info": update_info}, + ) + except Exception as exc: # noqa: BLE001 + post_error.append(exc) + + t_update = threading.Thread(target=_post_update, daemon=True) + t_update.start() + + assert NCCLWeightTransferEngine is not None + assert NCCLTrainerSendWeightsArgs is not None + NCCLWeightTransferEngine.trainer_send_weights( + iterator=iter(items), + trainer_args=NCCLTrainerSendWeightsArgs( + group=self._model_update_group, + packed=True, + ), + ) + + t_update.join(timeout=1800) + if t_update.is_alive(): + raise TimeoutError("Timed out waiting for vLLM /update_weights") + if post_error: + raise RuntimeError( + f"vLLM /update_weights failed: {post_error[0]}" + ) from post_error[0] def update_model_version(self, version: int) -> None: with self._lock: self._model_version = version + def _post_json( + self, + path: str, + *, + timeout: float, + json_body: dict[str, Any] | None = None, + params: dict[str, Any] | None = None, + ) -> requests.Response: + response = requests.post( + f"{self._vllm_base_url}{path}", + headers={"Authorization": f"Bearer {self._vllm_api_key}"}, + json=json_body, + params=params, + timeout=timeout, + ) + if response.status_code != 200: + raise RuntimeError( + f"{path} returned {response.status_code}: {response.text[:400]}" + ) + return response + + def _init_weight_transfer(self) -> None: + if ( + NCCLWeightTransferEngine is None + or NCCLTrainerSendWeightsArgs is None + or get_ip is None + or get_open_port is None + ): + _log.warning( + "vLLM NCCL weight-transfer modules unavailable; disabling sync" + ) + return + + response = requests.get( + f"{self._vllm_base_url}/get_world_size", + headers={"Authorization": f"Bearer {self._vllm_api_key}"}, + timeout=10, + ) + if response.status_code != 200: + raise RuntimeError( + "vLLM weight sync requires /get_world_size endpoint; " + "start vLLM with VLLM_SERVER_DEV_MODE=1 and " + '--weight-transfer-config \'{"backend":"nccl"}\'' + ) + + inference_world_size = int(response.json()["world_size"]) + world_size = inference_world_size + 1 + master_address = get_ip() + master_port = get_open_port() + + init_info = { + "master_address": master_address, + "master_port": master_port, + "rank_offset": 1, + "world_size": world_size, + } + + post_error: list[Exception] = [] + + def _post_init() -> None: + try: + self._post_json( + "/init_weight_transfer_engine", + timeout=120, + json_body={"init_info": init_info}, + ) + except Exception as exc: # noqa: BLE001 + post_error.append(exc) + + t_init = threading.Thread(target=_post_init, daemon=True) + t_init.start() + self._model_update_group = NCCLWeightTransferEngine.trainer_init( + { + "master_address": master_address, + "master_port": master_port, + "world_size": world_size, + } + ) + t_init.join(timeout=120) + if t_init.is_alive(): + raise TimeoutError("Timed out waiting for vLLM init_weight_transfer_engine") + if post_error: + raise RuntimeError( + f"vLLM init_weight_transfer_engine failed: {post_error[0]}" + ) from post_error[0] + + _log.info("initialized NCCL weight-transfer group with vLLM") + + def _destroy_model_update_group(self) -> None: + group = self._model_update_group + if group is None: + return + try: + group.group.store = None + group.group.socket = None + except Exception: + pass + self._model_update_group = None + # ── Internal ─────────────────────────────────────────────────── def _next_task(self) -> SWETask: @@ -203,23 +407,27 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None turns = 0 answer_called = False + pending_intercept: dict[str, Any] | None = None + rollout_stop_reason = "max_turns" t0 = time.time() try: while turns < self._cfg.max_turns and not self._stop.is_set(): - intercept = await session.next_request( - timeout_s=self._cfg.request_timeout_s, - ) + if pending_intercept is not None: + intercept = pending_intercept + pending_intercept = None + else: + intercept = await session.next_request( + timeout_s=self._cfg.request_timeout_s, + ) if intercept is None: + rollout_stop_reason = "agent_exit_detected" break # ── Tokenize this turn's full prompt ────────────── messages = _get_messages(intercept) - current_prompt_ids = self._tokenizer.apply_chat_template( - messages, - add_generation_prompt=True, - return_dict=False, - ) + tools = _get_tools(intercept) + current_prompt_ids = self._render_prompt_ids(messages, tools) if initial_prompt_ids is None: # First turn: the entire prompt is non-completion tokens. @@ -241,7 +449,9 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None turns += 1 # ── Generate via /v1/completions ────────────────── - turn_ids, turn_lps, text = self._generate(current_prompt_ids) + turn_ids, turn_lps, text, finish_reason = self._generate( + current_prompt_ids + ) all_ids.extend(turn_ids) all_mask.extend([1] * len(turn_ids)) @@ -251,7 +461,16 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None prev_prompt_ids = current_prompt_ids + turn_ids # ── Build chat response for Pi ──────────────────── - chat_resp = _make_chat_response(text, self._vllm_model) + assistant_message = _parse_assistant_message( + tokenizer=self._tokenizer, + completion_ids=turn_ids, + fallback_text=text, + ) + chat_resp = _make_chat_response( + assistant_message, + self._vllm_model, + finish_reason=finish_reason, + ) # ── Check for answer tool call ──────────────────── if _has_answer_call(chat_resp): @@ -259,13 +478,42 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None await session.deliver(intercept, chat_resp) + # Host-side answer handler marks this as soon as Pi executes answer(). + if bool(getattr(session, "answer_called", False)): + answer_called = True + if answer_called: + rollout_stop_reason = "answer_called" break + # Semantic completion fast-path (especially important on HF + # Sandbox, where process-exit detection can be delayed). + if ( + self._cfg.stop_on_idle_terminal_response + and self._cfg.post_response_grace_s > 0 + and _is_terminal_non_tool_response(chat_resp) + ): + try: + maybe_next = await session.next_request( + timeout_s=self._cfg.post_response_grace_s, + ) + except TimeoutError: + rollout_stop_reason = "idle_after_terminal_stop" + _log.info( + "rollout terminal idle-stop: instance_id=%s turns=%d", + task.instance_id, + turns, + ) + break + + if maybe_next is None: + rollout_stop_reason = "agent_exit_after_terminal_stop" + break + pending_intercept = maybe_next + # ── Reward ──────────────────────────────────────────── vr = session.verify(transcript=[]) reward = float(getattr(vr, "env_reward", 0.0) or 0.0) - metrics = dict(getattr(vr, "metrics", {}) or {}) if not all_lps: pad = getattr(self._tokenizer, "pad_token_id", 0) or 0 @@ -283,6 +531,13 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None "reward": reward, "turns": float(turns), "answer_called": float(answer_called), + "terminal_idle_stop": float( + rollout_stop_reason + in { + "idle_after_terminal_stop", + "agent_exit_after_terminal_stop", + } + ), "wall_s": round(time.time() - t0, 3), "n_tokens": float(len(all_ids)), }, @@ -292,10 +547,33 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None # ── vLLM call (matches TRL's _generate_one_turn exactly) ────── + def _render_prompt_ids( + self, + messages: list[dict[str, Any]], + tools: list[dict[str, Any]] | None, + ) -> list[int]: + kwargs: dict[str, Any] = { + "add_generation_prompt": True, + "return_dict": False, + } + # ``tools=[]`` can trigger unwanted boilerplate in some templates. + if tools: + kwargs["tools"] = tools + try: + ids = self._tokenizer.apply_chat_template(messages, **kwargs) + except TypeError: + kwargs.pop("tools", None) + ids = self._tokenizer.apply_chat_template(messages, **kwargs) + return cast(list[int], ids) + def _generate( - self, prompt_ids: list[int], - ) -> tuple[list[int], list[float], str]: - """POST /v1/completions with token IDs. Returns (ids, logprobs, text).""" + self, + prompt_ids: list[int], + ) -> tuple[list[int], list[float], str, str | None]: + """POST /v1/completions with token IDs. + + Returns: ``(token_ids, token_logprobs, text, finish_reason)``. + """ body = { "model": self._vllm_model, "prompt": prompt_ids, @@ -322,11 +600,13 @@ def _generate( choice["token_ids"], choice["logprobs"]["token_logprobs"], choice.get("text", ""), + choice.get("finish_reason"), ) # ── Helpers ──────────────────────────────────────────────────────────── + def _get_messages(intercept: dict[str, Any]) -> list[dict[str, Any]]: msgs = intercept.get("messages") if isinstance(msgs, list) and msgs: @@ -338,21 +618,118 @@ def _get_messages(intercept: dict[str, Any]) -> list[dict[str, Any]]: raise RuntimeError("intercept has no messages") -def _make_chat_response(text: str, model: str) -> dict[str, Any]: +def _get_tools(intercept: dict[str, Any]) -> list[dict[str, Any]] | None: + tools = intercept.get("tools") + if isinstance(tools, list): + return [t for t in tools if isinstance(t, dict)] + body = intercept.get("body") or {} + tools = body.get("tools") + if isinstance(tools, list): + return [t for t in tools if isinstance(t, dict)] + return None + + +def _normalize_tool_calls(raw_tool_calls: Any) -> list[dict[str, Any]]: + normalized: list[dict[str, Any]] = [] + if not isinstance(raw_tool_calls, list): + return normalized + + for raw_call in raw_tool_calls: + if not isinstance(raw_call, dict): + continue + function = raw_call.get("function") + if not isinstance(function, dict): + continue + name = function.get("name") + if not isinstance(name, str) or not name: + continue + + arguments = function.get("arguments", {}) + if isinstance(arguments, str): + arguments_str = arguments + else: + arguments_str = json.dumps(arguments or {}, ensure_ascii=False) + + normalized.append( + { + "id": str(raw_call.get("id") or f"call_{uuid.uuid4().hex[:8]}"), + "type": "function", + "function": { + "name": name, + "arguments": arguments_str, + }, + } + ) + + return normalized + + +def _parse_assistant_message( + *, + tokenizer: Any, + completion_ids: list[int], + fallback_text: str, +) -> dict[str, Any]: + parsed: dict[str, Any] | None = None + if parse_response is not None: + try: + maybe = parse_response(tokenizer, completion_ids) + if isinstance(maybe, dict): + parsed = maybe + except Exception: + parsed = None + + if parsed is None: + return {"role": "assistant", "content": fallback_text} + + content = parsed.get("content") + if content is None: + content = "" + if not isinstance(content, str): + content = str(content) + + message: dict[str, Any] = { + "role": "assistant", + "content": content, + } + tool_calls = _normalize_tool_calls(parsed.get("tool_calls")) + if tool_calls: + message["tool_calls"] = tool_calls + return message + + +def _make_chat_response( + assistant_message: dict[str, Any], + model: str, + *, + finish_reason: str | None = "stop", +) -> dict[str, Any]: return { "id": f"chatcmpl-{uuid.uuid4().hex[:8]}", "object": "chat.completion", "model": model, - "choices": [{ - "index": 0, - "message": {"role": "assistant", "content": text}, - "finish_reason": "stop", - }], + "choices": [ + { + "index": 0, + "message": assistant_message, + "finish_reason": finish_reason or "stop", + } + ], } +def _is_terminal_non_tool_response(resp: dict[str, Any]) -> bool: + for choice in resp.get("choices") or []: + message = (choice or {}).get("message") or {} + if message.get("tool_calls") or []: + return False + if (choice or {}).get("finish_reason") == "stop": + return True + return False + + def _has_answer_call(resp: dict[str, Any]) -> bool: - for choice in (resp.get("choices") or []): + for choice in resp.get("choices") or []: for tc in ((choice or {}).get("message") or {}).get("tool_calls") or []: if ((tc or {}).get("function") or {}).get("name") == "answer": return True From f9c8573b421ea29560a5647e7171a8b214c04137 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 16:08:30 +0530 Subject: [PATCH 44/79] chore: pin vLLM to 0.18.1 and update installation process in Dockerfile - test for hf sandbox backend & async rollout worker --- .../async_grpo/space_app/Dockerfile | 24 +++++-- .../async_grpo/space_app/start.sh | 5 +- envs/mini_swe_env/pyproject.toml | 12 ++-- tests/core/test_hf_sandbox_backend.py | 6 ++ tests/envs/test_swe_async_rollout_worker.py | 63 +++++++++++++++++++ 5 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 tests/envs/test_swe_async_rollout_worker.py diff --git a/envs/mini_swe_env/async_grpo/space_app/Dockerfile b/envs/mini_swe_env/async_grpo/space_app/Dockerfile index a995f11a6..a741f3095 100644 --- a/envs/mini_swe_env/async_grpo/space_app/Dockerfile +++ b/envs/mini_swe_env/async_grpo/space_app/Dockerfile @@ -1,4 +1,6 @@ -FROM vllm/vllm-openai:latest +# Pin vLLM to 0.18.1 — TRL AsyncGRPO supports 0.12-0.18.x for NCCL weight sync. +# vLLM 0.19+ has breaking changes in the weight transfer API. +FROM vllm/vllm-openai:v0.18.1 # System deps + Docker CLI (for Docker sandbox backend) RUN apt-get update -qq && \ @@ -21,10 +23,20 @@ RUN mkdir -p /home/user/.cache/huggingface && chown -R user:user /home/user/.cac WORKDIR $HOME/app COPY --chown=user . . -# Install: openenv-core + mini_swe_env[train] into system Python -RUN uv pip install --system --no-cache \ - -e "." \ - -e "envs/mini_swe_env[train]" +# Install deps: force-upgrade huggingface-hub first (resolves vLLM <1.0 vs hf-sandbox >=1.5 conflict) +# vLLM 0.18.1 works fine with newer huggingface-hub at runtime. +RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ + uv pip install --system --no-cache --no-deps \ + -e "." \ + -e "envs/mini_swe_env" && \ + uv pip install --system --no-cache \ + "trl>=1.4.0" \ + "accelerate>=1.13.0" \ + "hf-sandbox>=0.1.1" \ + "datasets>=4.8.0" \ + "aiohttp>=3.13.5" \ + "fastmcp>=3.3.0" \ + "trackio>=0.25.0" # Runtime defaults ENV PYTHONPATH=$HOME/app/src:$HOME/app/envs \ @@ -36,4 +48,4 @@ USER user EXPOSE 7860 ENTRYPOINT [] -CMD ["bash", "envs/mini_swe_env/async_grpo/space_app/start.sh"] +CMD ["bash", "start.sh"] diff --git a/envs/mini_swe_env/async_grpo/space_app/start.sh b/envs/mini_swe_env/async_grpo/space_app/start.sh index c2f5fbcee..0bb8f002c 100755 --- a/envs/mini_swe_env/async_grpo/space_app/start.sh +++ b/envs/mini_swe_env/async_grpo/space_app/start.sh @@ -34,13 +34,16 @@ echo "========================================" # ── 1. Start vLLM ───────────────────────────────────────────── echo "[start.sh] Starting vLLM on GPU $VLLM_GPU..." -CUDA_VISIBLE_DEVICES="$VLLM_GPU" vllm serve "$MODEL" \ +# Async GRPO weight sync requires vLLM dev mode + NCCL transfer endpoints. +CUDA_VISIBLE_DEVICES="$VLLM_GPU" VLLM_SERVER_DEV_MODE=1 vllm serve "$MODEL" \ --tensor-parallel-size 1 \ --max-model-len "$MAX_MODEL_LEN" \ --host 127.0.0.1 \ --port "$VLLM_PORT" \ --api-key "$VLLM_KEY" \ --gpu-memory-utilization "$GPU_MEM_UTIL" \ + --logprobs-mode processed_logprobs \ + --weight-transfer-config '{"backend":"nccl"}' \ > /tmp/vllm.log 2>&1 & VLLM_PID=$! diff --git a/envs/mini_swe_env/pyproject.toml b/envs/mini_swe_env/pyproject.toml index 4148338db..6fc482e85 100644 --- a/envs/mini_swe_env/pyproject.toml +++ b/envs/mini_swe_env/pyproject.toml @@ -27,18 +27,14 @@ dependencies = [ [project.optional-dependencies] train = [ - "torch>=2.6.0", "trl>=1.4.0", - "transformers>=5.8.0", "accelerate>=1.13.0", - "trackio>=0.25.0", "hf-sandbox>=0.1.1", + "datasets>=4.8.0", + "aiohttp>=3.13.5", + "fastmcp>=3.3.0", ] -dev = [ - "pytest>=8.0.0", - "pytest-asyncio>=0.23.0", - "pytest-cov>=4.0.0", -] +dev = ["pytest>=8.0.0", "pytest-asyncio>=0.23.0", "pytest-cov>=4.0.0"] [project.scripts] server = "mini_swe_env.server.app:main" diff --git a/tests/core/test_hf_sandbox_backend.py b/tests/core/test_hf_sandbox_backend.py index 9cd94b5d8..6ad8e3fb2 100644 --- a/tests/core/test_hf_sandbox_backend.py +++ b/tests/core/test_hf_sandbox_backend.py @@ -200,7 +200,13 @@ def test_create_exec_write_read_exists_bg_and_kill(self, monkeypatch): short_job = sandbox.start_bg("echo done > /tmp/bg.txt") assert short_job.wait(timeout=2) == 0 + # timeout=0 should still perform one probe and detect completed jobs. + short_job_nonblocking = sandbox.start_bg("echo done > /tmp/bg_nowait.txt") + assert short_job_nonblocking.wait(timeout=0) == 0 + long_job = sandbox.start_bg("sleep 300") + with pytest.raises(TimeoutError): + long_job.wait(timeout=0) with pytest.raises(TimeoutError): long_job.wait(timeout=0.1) long_job.kill() diff --git a/tests/envs/test_swe_async_rollout_worker.py b/tests/envs/test_swe_async_rollout_worker.py new file mode 100644 index 000000000..772bf9629 --- /dev/null +++ b/tests/envs/test_swe_async_rollout_worker.py @@ -0,0 +1,63 @@ +from mini_swe_env.async_grpo.rollout_worker import ( + _get_tools, + _has_answer_call, + _is_terminal_non_tool_response, + _make_chat_response, + _normalize_tool_calls, +) + + +def test_normalize_tool_calls_serializes_arguments_to_json_string() -> None: + calls = _normalize_tool_calls( + [ + { + "id": "call_1", + "type": "function", + "function": {"name": "answer", "arguments": {}}, + } + ] + ) + assert len(calls) == 1 + assert calls[0]["function"]["name"] == "answer" + assert calls[0]["function"]["arguments"] == "{}" + + +def test_terminal_detection_requires_stop_and_no_tool_calls() -> None: + terminal = _make_chat_response( + {"role": "assistant", "content": "Done."}, + model="qwen", + finish_reason="stop", + ) + assert _is_terminal_non_tool_response(terminal) is True + + with_tools = _make_chat_response( + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "answer", "arguments": "{}"}, + } + ], + }, + model="qwen", + finish_reason="stop", + ) + assert _is_terminal_non_tool_response(with_tools) is False + assert _has_answer_call(with_tools) is True + + +def test_get_tools_from_intercept_or_body() -> None: + tool_schema = { + "type": "function", + "function": { + "name": "answer", + "description": "submit", + "parameters": {"type": "object", "properties": {}}, + }, + } + assert _get_tools({"tools": [tool_schema]}) == [tool_schema] + assert _get_tools({"body": {"tools": [tool_schema]}}) == [tool_schema] + assert _get_tools({}) is None From 254f32a57ad3ef20a72d2cd8fc888f2c06027e78 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 16:21:58 +0530 Subject: [PATCH 45/79] refactor: replace asyncio.Queue with queue.Queue for interception queue handling --- envs/mini_swe_env/async_grpo/control_plane.py | 4 ++-- envs/mini_swe_env/harness.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/envs/mini_swe_env/async_grpo/control_plane.py b/envs/mini_swe_env/async_grpo/control_plane.py index f6bd64a2d..ecd425b99 100644 --- a/envs/mini_swe_env/async_grpo/control_plane.py +++ b/envs/mini_swe_env/async_grpo/control_plane.py @@ -1,7 +1,7 @@ from __future__ import annotations -import asyncio import contextlib +import queue as _queue_mod import logging import os from dataclasses import dataclass @@ -131,7 +131,7 @@ def register_rollout( rollout_id: str, *, state: dict[str, Any] | None = None, - ) -> asyncio.Queue: + ) -> _queue_mod.Queue[str]: queue = self.server.register_rollout(rollout_id, state=state) stats = self.stats() _log.info( diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index a76f2e360..e00255d1f 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -48,6 +48,7 @@ import asyncio import json +import queue as _queue_mod import logging import shlex import time @@ -502,7 +503,7 @@ def create( base_url_override: str | None = None interception_rollout_id: str | None = None - interception_queue: asyncio.Queue | None = None + interception_queue: _queue_mod.Queue[str] | None = None if self._mode == "interception_gate": assert self._interception_server is not None From f8d984be6696efd4c3bfaffa02a7758d49bd91e3 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 19:31:35 +0530 Subject: [PATCH 46/79] feat: checkpointing --- .../async_grpo/space_app/README.md | 13 +- .../async_grpo/space_app/start.sh | 2 + examples/mini_swe_env/train_swe_async_grpo.py | 176 +++++++++++++++--- 3 files changed, 168 insertions(+), 23 deletions(-) diff --git a/envs/mini_swe_env/async_grpo/space_app/README.md b/envs/mini_swe_env/async_grpo/space_app/README.md index 1fa5b8186..036a765f1 100644 --- a/envs/mini_swe_env/async_grpo/space_app/README.md +++ b/envs/mini_swe_env/async_grpo/space_app/README.md @@ -28,7 +28,18 @@ Set these in the Space Settings tab: | Secret | Description | |--------|-------------| -| `HF_TOKEN` | HF token for sandbox creation and model downloads | +| `HF_TOKEN` | HF token for sandbox creation, model downloads, and checkpoint uploads | | `INTERCEPTION_AUTH_TOKEN` | Shared auth token for Pi ↔ InterceptionServer | | `SWE_MODEL` | Model ID to serve and train (e.g. `Qwen/Qwen3-1.7B`) | + +## Optional Variables (recommended) + +| Variable | Description | +|----------|-------------| | `TRACKIO_SPACE_ID` | Trackio dashboard Space for metrics (e.g. `user/swe-grpo-dashboard`) | +| `SWE_CHECKPOINT_TO_HUB` | Enable checkpoint upload to HF Hub (`1`/`0`, default `1` on Spaces) | +| `SWE_HUB_MODEL_ID` | Hub model repo used as checkpoint bucket (e.g. `user/swe-async-grpo-checkpoints`) | +| `SWE_RESUME_FROM_CHECKPOINT` | `auto` (default) to resume from `last-checkpoint`, or explicit path/name | +| `SWE_CHECKPOINT_SAVE_STEPS` | Save/upload frequency in trainer steps (default `2`) | +| `SWE_CHECKPOINT_SAVE_TOTAL_LIMIT` | Number of local checkpoints to keep (default `2`) | +| `SWE_HUB_PRIVATE_REPO` | Create/use private checkpoint repo (`1`/`0`, default `1`) | diff --git a/envs/mini_swe_env/async_grpo/space_app/start.sh b/envs/mini_swe_env/async_grpo/space_app/start.sh index 0bb8f002c..7d69f7215 100755 --- a/envs/mini_swe_env/async_grpo/space_app/start.sh +++ b/envs/mini_swe_env/async_grpo/space_app/start.sh @@ -30,6 +30,8 @@ echo "Max model len: $MAX_MODEL_LEN" echo "GPU mem util: $GPU_MEM_UTIL" echo "vLLM GPU: $VLLM_GPU" echo "Trainer GPU: $TRAINER_GPU" +echo "Checkpointing: ${SWE_CHECKPOINT_TO_HUB:-auto}" +echo "Checkpoint repo: ${SWE_HUB_MODEL_ID:-}" echo "========================================" # ── 1. Start vLLM ───────────────────────────────────────────── diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 6d2598563..06157bb7d 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -27,7 +27,7 @@ import argparse import asyncio -import json +import inspect import logging import os import sys @@ -127,6 +127,99 @@ def _env(name: str) -> str: return v +def _bool_env(name: str, default: bool) -> bool: + raw = os.environ.get(name) + if raw is None: + return default + return raw.strip().lower() in {"1", "true", "yes", "on"} + + +def _int_env(name: str, default: int, *, min_value: int = 1) -> int: + raw = os.environ.get(name) + if raw is None or not raw.strip(): + return default + value = int(raw) + if value < min_value: + raise ValueError(f"{name} must be >= {min_value}, got {value}") + return value + + +def _derive_checkpoint_repo_id() -> str | None: + explicit = os.environ.get("SWE_HUB_MODEL_ID", "").strip() + if explicit: + return explicit + space_id = ( + os.environ.get("HF_SPACE_ID", "").strip() + or os.environ.get("SPACE_ID", "").strip() + ) + if "/" not in space_id: + return None + return f"{space_id}-checkpoints" + + +def _build_checkpoint_args() -> tuple[dict[str, Any], str | None, bool]: + in_space = bool( + os.environ.get("HF_SPACE_ID") + or os.environ.get("SPACE_ID") + or os.environ.get("SPACE_HOST") + ) + enabled = _bool_env("SWE_CHECKPOINT_TO_HUB", default=in_space) + if not enabled: + return {}, None, False + + repo_id = _derive_checkpoint_repo_id() + if not repo_id: + _log.warning( + "checkpointing requested, but SWE_HUB_MODEL_ID/HF_SPACE_ID missing; " + "disabling hub checkpointing" + ) + return {}, None, False + + save_steps = _int_env("SWE_CHECKPOINT_SAVE_STEPS", default=2) + save_total_limit = _int_env("SWE_CHECKPOINT_SAVE_TOTAL_LIMIT", default=2) + + checkpoint_args = { + "save_strategy": "steps", + "save_steps": save_steps, + "save_total_limit": save_total_limit, + "push_to_hub": True, + "hub_model_id": repo_id, + "hub_strategy": "checkpoint", + "hub_private_repo": _bool_env("SWE_HUB_PRIVATE_REPO", default=True), + "hub_token": os.environ.get("HF_TOKEN", "").strip() or None, + } + + resume_pref = os.environ.get("SWE_RESUME_FROM_CHECKPOINT", "auto").strip().lower() + if resume_pref in {"", "0", "false", "off", "none"}: + resume_from_checkpoint: str | None = None + elif resume_pref == "auto": + resume_from_checkpoint = "last-checkpoint" + else: + resume_from_checkpoint = os.environ.get("SWE_RESUME_FROM_CHECKPOINT", "").strip() + + return checkpoint_args, resume_from_checkpoint, True + + +def _filter_async_grpo_kwargs(values: dict[str, Any]) -> dict[str, Any]: + sig = inspect.signature(AsyncGRPOConfig) + return {k: v for k, v in values.items() if k in sig.parameters} + + +def _is_missing_checkpoint_error(exc: Exception) -> bool: + msg = str(exc).lower() + if "last-checkpoint" not in msg: + return False + hints = ( + "no valid checkpoint", + "can't find", + "cannot find", + "does not exist", + "not found", + "404", + ) + return any(hint in msg for hint in hints) + + def main() -> int: logging.basicConfig( level=logging.INFO, @@ -200,35 +293,74 @@ def _noop_reward(**kwargs: Any) -> list[float]: prompts = kwargs.get("prompts", []) return [0.0] * len(prompts) + checkpoint_args, resume_from_checkpoint, checkpoint_requested = _build_checkpoint_args() + async_grpo_args: dict[str, Any] = { + "output_dir": os.path.join( + os.environ.get("HOME", "/tmp"), "outputs/swe_async_grpo" + ), + "vllm_server_base_url": vllm_url, + "vllm_server_timeout": 2400.0, + "max_completion_length": 2048, + "max_steps": args.max_steps, + "per_device_train_batch_size": 1, + "gradient_accumulation_steps": 1, + "num_generations": 1, + "learning_rate": 1e-6, + "temperature": 1.0, + "max_staleness": 4, + "weight_sync_steps": 1, + "max_inflight_tasks": 2, + "logging_steps": 1, + "report_to": "trackio", + "run_name": f"swe-grpo-{model.split('/')[-1]}", + "trackio_space_id": os.environ.get("TRACKIO_SPACE_ID", "").strip() or None, + } + filtered_checkpoint_args = _filter_async_grpo_kwargs(checkpoint_args) + async_grpo_args.update(filtered_checkpoint_args) + + checkpoint_enabled = bool(filtered_checkpoint_args.get("push_to_hub")) + if checkpoint_requested and not checkpoint_enabled: + _log.warning( + "checkpointing requested, but AsyncGRPOConfig does not expose hub args; " + "continuing without hub checkpointing" + ) + resume_from_checkpoint = None + trainer = AsyncGRPOTrainer( model=model, reward_funcs=_noop_reward, train_dataset=dataset, processing_class=tokenizer, rollout_worker=worker, - args=AsyncGRPOConfig( - output_dir=os.path.join(os.environ.get("HOME", "/tmp"), "outputs/swe_async_grpo"), - vllm_server_base_url=vllm_url, - vllm_server_timeout=2400.0, - max_completion_length=2048, - max_steps=args.max_steps, - per_device_train_batch_size=1, - gradient_accumulation_steps=1, - num_generations=1, - learning_rate=1e-6, - temperature=1.0, - max_staleness=4, - weight_sync_steps=1, - max_inflight_tasks=2, - logging_steps=1, - report_to="trackio", - run_name=f"swe-grpo-{model.split('/')[-1]}", - trackio_space_id=os.environ.get("TRACKIO_SPACE_ID", "").strip() or None, - ), + args=AsyncGRPOConfig(**async_grpo_args), + ) + + _log.info( + "starting training: model=%s tasks=%d checkpointing=%s resume=%s", + model, + len(swe_tasks), + checkpoint_enabled, + resume_from_checkpoint or "none", ) + if resume_from_checkpoint is None: + trainer.train() + else: + try: + trainer.train(resume_from_checkpoint=resume_from_checkpoint) + except Exception as exc: + if resume_from_checkpoint == "last-checkpoint" and _is_missing_checkpoint_error(exc): + _log.warning( + "No hub checkpoint found at 'last-checkpoint'; starting from scratch" + ) + trainer.train() + else: + raise + + if checkpoint_enabled and hasattr(trainer, "push_to_hub"): + trainer.push_to_hub( + commit_message=f"Final checkpoint at step {getattr(trainer.state, 'global_step', '?')}" + ) - _log.info("starting training: model=%s tasks=%d", model, len(swe_tasks)) - trainer.train() _log.info("done: step=%s", getattr(trainer.state, "global_step", "?")) return 0 finally: From b10a4483a2309d7bdbd5fa5aacb5cfe34a082209 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 21:13:48 +0530 Subject: [PATCH 47/79] fix: replace asyncio.Queue with queue.Queue for thread-safe request handling - soak test --- envs/coding_agent_env/harness.py | 4 +- tests/core/test_cli_agent_driver.py | 76 +++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/envs/coding_agent_env/harness.py b/envs/coding_agent_env/harness.py index 748dcb091..379a055bb 100644 --- a/envs/coding_agent_env/harness.py +++ b/envs/coding_agent_env/harness.py @@ -8,7 +8,7 @@ from __future__ import annotations -import asyncio +import queue as _queue_mod import uuid from typing import Any, Literal @@ -126,7 +126,7 @@ def create( # Wire up interception_gate if the driver is configured for it base_url_override: str | None = None interception_rollout_id: str | None = None - interception_queue: asyncio.Queue | None = None + interception_queue: _queue_mod.Queue[str] | None = None if self._driver.mode == "interception_gate": assert self._driver._interception_server is not None diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index 18854fe7e..6c1c1511e 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -17,8 +17,11 @@ from __future__ import annotations +import asyncio import json import queue as _queue_mod +import threading +import time from dataclasses import dataclass, field from typing import Any @@ -810,6 +813,79 @@ async def test_next_request_handles_missing_intercept_without_keyerror(self): # Missing request IDs can happen if unregister_rollout races with queue.get(). assert await session.next_request(timeout_s=0.2) is None + def test_next_request_soak_cross_loop_queue_get(self): + """Soak test cross-loop request dequeueing via queue.Queue. + + Exercises the worker pattern that used to be unsafe with asyncio.Queue: + repeatedly call next_request() from fresh event loops (asyncio.run) + while request IDs are pushed from another thread. + """ + from openenv.core.harness.agents.cli_driver import CLIAgentSession + from openenv.core.harness.agents.interception_server import InterceptionServer + + spec = _make_test_spec() + sbx = FakeSandbox() + server = InterceptionServer(secret="s") + request_queue = server.register_rollout("rollout-soak") + + session = CLIAgentSession( + spec=spec, + sandbox=sbx, + task=FakeTask(), + config=FakeConfig(), + interception_server=server, + interception_rollout_id="rollout-soak", + interception_queue=request_queue, + ) + + total_requests = 200 + consumed: list[str] = [] + failures: list[BaseException] = [] + + def _consumer() -> None: + try: + for _ in range(total_requests): + intercept = asyncio.run(session.next_request(timeout_s=2.0)) + assert intercept is not None + request_id = intercept["request_id"] + consumed.append(request_id) + with server._state_lock: + server.intercepts.pop(request_id, None) + except BaseException as exc: # pragma: no cover - assertion path + failures.append(exc) + + def _producer() -> None: + try: + for i in range(total_requests): + request_id = f"req_soak_{i:04d}" + with server._state_lock: + server.intercepts[request_id] = { + "request_id": request_id, + "messages": [{"role": "user", "content": "ping"}], + } + request_queue.put_nowait(request_id) + if i % 10 == 0: + time.sleep(0.001) + except BaseException as exc: # pragma: no cover - unexpected + failures.append(exc) + + consumer_t = threading.Thread(target=_consumer, name="soak-consumer") + producer_t = threading.Thread(target=_producer, name="soak-producer") + + consumer_t.start() + producer_t.start() + + producer_t.join(timeout=10) + consumer_t.join(timeout=15) + + assert not producer_t.is_alive(), "producer thread hung" + assert not consumer_t.is_alive(), "consumer thread hung" + assert not failures + assert len(consumed) == total_requests + assert len(set(consumed)) == total_requests + + session.close() + class TestCLIAgentSessionFactory: """Tests for the ResourceSessionFactory wrapper.""" From 659288b5e8fd0680253d65ba5a31648a13c242da Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 21:23:35 +0530 Subject: [PATCH 48/79] fix: pi config discovery for CLIAgentDriver to be independent of runtime user's $HOME --- src/openenv/core/harness/agents/cli_driver.py | 12 +++---- tests/core/test_cli_agent_driver.py | 32 +++++++++++++++++-- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index a2724162e..831d930af 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -494,6 +494,10 @@ def _start_agent( else: cmd = " ".join(shlex.quote(c) for c in self.spec.base_command) envs = self._resolve_env_vars(config, base_url_override=base_url_override) + if self.spec.name == "pi": + home = self._resolve_sandbox_home(sandbox, config) + # Make pi config discovery independent of the runtime user's $HOME. + envs["PI_CODING_AGENT_DIR"] = f"{home}/.pi/agent" if self.mode == "interception_gate" and self._interception_server is not None: envs["OPENAI_API_KEY"] = self._interception_server.secret envs["ANTHROPIC_API_KEY"] = self._interception_server.secret @@ -507,7 +511,7 @@ def _write_pi_models_config( rollout_url: str, api_key: str, ) -> None: - home = config.sandbox_home if hasattr(config, "sandbox_home") else "/home/user" + home = self._resolve_sandbox_home(sandbox, config) model = config.model if hasattr(config, "model") else "model" content = json.dumps( { @@ -526,11 +530,7 @@ def _write_pi_models_config( }, indent=2, ) - paths = {f"{home}/.pi/agent/models.json"} - if home == "/root": - paths.add("/root/.pi/agent/models.json") - for path in paths: - sandbox.write_text(path, content) + sandbox.write_text(f"{home}/.pi/agent/models.json", content) def _resolve_env_vars( self, diff --git a/tests/core/test_cli_agent_driver.py b/tests/core/test_cli_agent_driver.py index 6c1c1511e..7338fc323 100644 --- a/tests/core/test_cli_agent_driver.py +++ b/tests/core/test_cli_agent_driver.py @@ -583,13 +583,14 @@ def test_pi_interception_gate_writes_models_json_and_uses_openenv_provider(self) sbx = backend.created[0] # Command should force the custom provider backed by models.json. - cmd, _envs = sbx.bg_commands[-1] + cmd, envs = sbx.bg_commands[-1] assert "--provider openenv" in cmd + assert envs is not None + assert envs["PI_CODING_AGENT_DIR"] == "/home/user/.pi/agent" home_models = "/home/user/.pi/agent/models.json" root_models = "/root/.pi/agent/models.json" assert home_models in sbx.written - # /root/ path is only written when sandbox_home == "/root" assert root_models not in sbx.written cfg = json.loads(sbx.written[home_models]) @@ -602,6 +603,33 @@ def test_pi_interception_gate_writes_models_json_and_uses_openenv_provider(self) session.close() + def test_pi_interception_gate_uses_explicit_pi_config_dir(self): + from openenv.core.harness.agents.cli_driver import CLIAgentDriver + from openenv.core.harness.agents.interception_server import InterceptionServer + from openenv.core.harness.agents.pi import PI_SPEC + + backend = FakeSandboxBackend() + server = InterceptionServer(port=0, secret="gate-secret") + driver = CLIAgentDriver( + spec=PI_SPEC, + sandbox_backend=backend, + mode="interception_gate", + interception_server=server, + interception_base_url="http://127.0.0.1:8765", + ) + + config = FakeConfig(sandbox_home="/custom/home") + session = driver.create_session(task=FakeTask(), config=config) + sbx = backend.created[0] + + _cmd, envs = sbx.bg_commands[-1] + assert envs is not None + assert envs["PI_CODING_AGENT_DIR"] == "/custom/home/.pi/agent" + assert "/custom/home/.pi/agent/models.json" in sbx.written + assert "/root/.pi/agent/models.json" not in sbx.written + + session.close() + def test_create_session_runs_task_setup_shell(self): from openenv.core.harness.agents.cli_driver import CLIAgentDriver From 500413a1324648cf281c745095be7a62e8b0eb91 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 21:38:44 +0530 Subject: [PATCH 49/79] feat: bitsandbytes training params --- envs/mini_swe_env/async_grpo/space_app/Dockerfile | 3 ++- envs/mini_swe_env/pyproject.toml | 1 + examples/mini_swe_env/train_swe_async_grpo.py | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/envs/mini_swe_env/async_grpo/space_app/Dockerfile b/envs/mini_swe_env/async_grpo/space_app/Dockerfile index a741f3095..43f734e6f 100644 --- a/envs/mini_swe_env/async_grpo/space_app/Dockerfile +++ b/envs/mini_swe_env/async_grpo/space_app/Dockerfile @@ -36,7 +36,8 @@ RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ "datasets>=4.8.0" \ "aiohttp>=3.13.5" \ "fastmcp>=3.3.0" \ - "trackio>=0.25.0" + "trackio>=0.25.0" \ + "bitsandbytes>=0.43.0" # Runtime defaults ENV PYTHONPATH=$HOME/app/src:$HOME/app/envs \ diff --git a/envs/mini_swe_env/pyproject.toml b/envs/mini_swe_env/pyproject.toml index 6fc482e85..3a641b505 100644 --- a/envs/mini_swe_env/pyproject.toml +++ b/envs/mini_swe_env/pyproject.toml @@ -23,6 +23,7 @@ dependencies = [ "requests>=2.25.0", "aiohttp>=3.13.5", "datasets>=4.8.0", + "bitsandbytes>=0.43.0", ] [project.optional-dependencies] diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 06157bb7d..277153fcf 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -307,6 +307,9 @@ def _noop_reward(**kwargs: Any) -> list[float]: "num_generations": 1, "learning_rate": 1e-6, "temperature": 1.0, + "optim": "adamw_bnb_8bit", + "bf16": True, + "gradient_checkpointing": True, "max_staleness": 4, "weight_sync_steps": 1, "max_inflight_tasks": 2, From 17569087590b1431b91a70ec211c6053e2e84954 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 18 May 2026 22:03:30 +0530 Subject: [PATCH 50/79] feat: deploy script for SWE Async GRPO training to HF Space --- .../async_grpo/space_app/deploy_hf_space.sh | 314 ++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100755 envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh diff --git a/envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh new file mode 100755 index 000000000..8430a1deb --- /dev/null +++ b/envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -0,0 +1,314 @@ +#!/bin/bash +# deploy_hf_space.sh — Deploy SWE Async GRPO training to HF Space + HF Sandbox +# +# This script: +# 1. Prepares the Space directory (minimal repo subset) +# 2. Creates/updates the HF Space +# 3. Configures secrets and environment variables +# 4. Sets hardware (a10g-largex2) +# 5. Pushes code and triggers build +# 6. Monitors build/startup +# +# Prerequisites: +# - hf CLI installed and authenticated (hf auth login) +# - HF_TOKEN set or in ~/.cache/huggingface/token +# - Python with huggingface_hub installed +# +# Usage: +# bash envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh [OPTIONS] +# +# Options: +# --space-id OWNER/NAME Space ID (default: $HF_SPACE_ID or rycerzes/swe-async-grpo-train) +# --model MODEL_ID Model to train (default: Qwen/Qwen3-0.6B) +# --max-tasks N Number of SWE tasks (default: 5) +# --max-steps N Training steps (default: 10) +# --max-turns N Max agent turns per rollout (default: 30) +# --hardware HW Hardware tier (default: a10g-largex2) +# --sandbox-backend BACKEND Backend for agent (default: hf) +# --skip-build Only configure, don't push code +# --monitor Monitor logs after deploy +# --pause Pause the Space (stop billing) +# --resume Resume a paused Space +# +set -euo pipefail + +# ── Defaults ──────────────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)" + +SPACE_ID="${HF_SPACE_ID:-rycerzes/swe-async-grpo-train}" +MODEL="${SWE_MODEL:-Qwen/Qwen3-1.7B}" +MAX_TASKS=5 +MAX_STEPS=10 +MAX_TURNS=30 +HARDWARE="a10g-largex2" +SANDBOX_BACKEND="hf" +SKIP_BUILD=false +MONITOR=false +PAUSE=false +RESUME=false + +# ── Parse args ────────────────────────────────────────────────────────── +while [[ $# -gt 0 ]]; do + case "$1" in + --space-id) SPACE_ID="$2"; shift 2 ;; + --model) MODEL="$2"; shift 2 ;; + --max-tasks) MAX_TASKS="$2"; shift 2 ;; + --max-steps) MAX_STEPS="$2"; shift 2 ;; + --max-turns) MAX_TURNS="$2"; shift 2 ;; + --hardware) HARDWARE="$2"; shift 2 ;; + --sandbox-backend) SANDBOX_BACKEND="$2"; shift 2 ;; + --skip-build) SKIP_BUILD=true; shift ;; + --monitor) MONITOR=true; shift ;; + --pause) PAUSE=true; shift ;; + --resume) RESUME=true; shift ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +# ── Resolve HF_TOKEN ──────────────────────────────────────────────────── +if [ -z "${HF_TOKEN:-}" ]; then + if [ -f "$HOME/.cache/huggingface/token" ]; then + HF_TOKEN=$(cat "$HOME/.cache/huggingface/token") + else + echo "ERROR: HF_TOKEN not set and not found in ~/.cache/huggingface/token" + echo "Run: hf auth login" + exit 1 + fi +fi +export HF_TOKEN + +# ── Pause/Resume shortcuts ────────────────────────────────────────────── +if [ "$PAUSE" = true ]; then + echo "⏸ Pausing Space $SPACE_ID..." + python3 -c " +from huggingface_hub import HfApi +api = HfApi(token='$HF_TOKEN') +api.pause_space('$SPACE_ID') +print('Space paused.') +" + exit 0 +fi + +if [ "$RESUME" = true ]; then + echo "▶ Resuming Space $SPACE_ID..." + python3 -c " +from huggingface_hub import HfApi +api = HfApi(token='$HF_TOKEN') +api.restart_space('$SPACE_ID', factory_reboot=True) +print('Space restarted.') +" + exit 0 +fi + +# ── Print config ──────────────────────────────────────────────────────── +echo "╔══════════════════════════════════════════════════════════╗" +echo "║ SWE Async GRPO — HF Space Deployment ║" +echo "╠══════════════════════════════════════════════════════════╣" +printf "║ %-54s ║\n" "Space: $SPACE_ID" +printf "║ %-54s ║\n" "Model: $MODEL" +printf "║ %-54s ║\n" "Hardware: $HARDWARE" +printf "║ %-54s ║\n" "Tasks: $MAX_TASKS" +printf "║ %-54s ║\n" "Steps: $MAX_STEPS" +printf "║ %-54s ║\n" "Turns: $MAX_TURNS" +printf "║ %-54s ║\n" "Sandbox: $SANDBOX_BACKEND" +echo "╚══════════════════════════════════════════════════════════╝" +echo "" + +# ── Step 1: Create Space (idempotent) ────────────────────────────────── +echo "[1/6] Creating Space (if needed)..." +hf repos create "$SPACE_ID" --type space --space-sdk docker --public --exist-ok 2>/dev/null || true +echo " ✓ Space exists: https://huggingface.co/spaces/$SPACE_ID" + +# ── Step 2: Configure secrets ────────────────────────────────────────── +echo "[2/6] Configuring secrets and variables..." +python3 << PYEOF +from huggingface_hub import HfApi +import os, secrets + +api = HfApi(token=os.environ['HF_TOKEN']) +space_id = "$SPACE_ID" +checkpoint_repo = f"{space_id}-checkpoints" + +# Create/ensure checkpoint bucket repo for restart-safe training state. +try: + api.create_repo(repo_id=checkpoint_repo, repo_type="model", private=True, exist_ok=True) + print(f" ✓ Checkpoint repo: {checkpoint_repo}") +except Exception as e: + print(f" ⚠ Checkpoint repo {checkpoint_repo}: {e}") + +# Secrets (sensitive) +space_secrets = { + "HF_TOKEN": os.environ["HF_TOKEN"], + "INTERCEPTION_AUTH_TOKEN": secrets.token_urlsafe(32), + "SWE_MODEL": "$MODEL", + "VLLM_API_KEY": "token", +} +for key, value in space_secrets.items(): + try: + api.add_space_secret(space_id, key, value) + print(f" ✓ Secret: {key}") + except Exception as e: + print(f" ⚠ Secret {key}: {e}") + +# Variables (non-sensitive) +variables = { + "MAX_MODEL_LEN": "4096", + "GPU_MEMORY_UTILIZATION": "0.50", + "VLLM_GPU": "0", + "TRAINER_GPU": "1", + "INTERCEPTION_HOST": "0.0.0.0", + "INTERCEPTION_PORT": "7860", + "TRL_EXPERIMENTAL_SILENCE": "1", + "TRACKIO_SPACE_ID": "rycerzes/swe-grpo-dashboard", + "SWE_CHECKPOINT_TO_HUB": "1", + "SWE_HUB_MODEL_ID": checkpoint_repo, + "SWE_RESUME_FROM_CHECKPOINT": "auto", + "SWE_CHECKPOINT_SAVE_STEPS": "2", + "SWE_CHECKPOINT_SAVE_TOTAL_LIMIT": "2", + "SWE_HUB_PRIVATE_REPO": "1", +} +for key, value in variables.items(): + try: + api.add_space_variable(space_id, key, value) + print(f" ✓ Var: {key}={value}") + except Exception as e: + print(f" ⚠ Var {key}: {e}") +PYEOF + +# ── Step 3: Set hardware ─────────────────────────────────────────────── +echo "[3/6] Setting hardware to $HARDWARE..." +python3 -c " +from huggingface_hub import HfApi +import os +api = HfApi(token=os.environ['HF_TOKEN']) +api.request_space_hardware('$SPACE_ID', '$HARDWARE') +print(' ✓ Hardware: $HARDWARE') +" + +# ── Step 4: Prepare staging directory ────────────────────────────────── +if [ "$SKIP_BUILD" = true ]; then + echo "[4/6] Skipping build (--skip-build)" + echo "[5/6] Skipping upload" +else + echo "[4/6] Preparing Space directory..." + STAGE_DIR=$(mktemp -d) + trap "rm -rf $STAGE_DIR" EXIT + + cd "$REPO_ROOT" + + # Copy essential files only + rsync -a --exclude='.git' --exclude='.venv' --exclude='__pycache__' \ + --exclude='.worktrees' --exclude='node_modules' --exclude='*.pyc' \ + --exclude='.tox' --exclude='.mypy_cache' --exclude='.pytest_cache' \ + --exclude='*.egg-info' --exclude='uv.lock' --exclude='.ruff_cache' \ + --exclude='docs' --exclude='tests' --exclude='.claude' --exclude='.agents' \ + --exclude='.codex' --exclude='.github' --exclude='rfcs' --exclude='tutorial' \ + --exclude='scripts' --exclude='.env' \ + src/ "$STAGE_DIR/src/" + + mkdir -p "$STAGE_DIR/envs" + rsync -a --exclude='__pycache__' --exclude='*.pyc' --exclude='uv.lock' \ + --exclude='.venv' \ + envs/mini_swe_env/ "$STAGE_DIR/envs/mini_swe_env/" + + mkdir -p "$STAGE_DIR/examples/mini_swe_env" + cp examples/mini_swe_env/train_swe_async_grpo.py "$STAGE_DIR/examples/mini_swe_env/" + + cp pyproject.toml "$STAGE_DIR/" + cp LICENSE "$STAGE_DIR/" + cp .gitignore "$STAGE_DIR/" + + # Space-specific files at root + cp envs/mini_swe_env/async_grpo/space_app/Dockerfile "$STAGE_DIR/Dockerfile" + cp envs/mini_swe_env/async_grpo/space_app/start.sh "$STAGE_DIR/start.sh" + + # Space README (metadata) + cat > "$STAGE_DIR/README.md" << EOF +--- +title: SWE Async GRPO Training +emoji: 🔧 +colorFrom: blue +colorTo: green +sdk: docker +app_port: 7860 +suggested_hardware: $HARDWARE +startup_duration_timeout: 30m +preload_from_hub: + - $MODEL +--- + +# SWE Async GRPO Training + +Model: \`$MODEL\` | Tasks: $MAX_TASKS | Steps: $MAX_STEPS | Backend: $SANDBOX_BACKEND +EOF + + # Patch start.sh to pass our training args + cat >> "$STAGE_DIR/start.sh" << EOF + +# Auto-generated training args from deploy script +# --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS +EOF + # Actually replace the exec line to include our args + sed -i "s|exec python3 examples/mini_swe_env/train_swe_async_grpo.py|exec python3 examples/mini_swe_env/train_swe_async_grpo.py --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS|" "$STAGE_DIR/start.sh" + + FILE_COUNT=$(find "$STAGE_DIR" -type f | wc -l) + echo " ✓ Staged $FILE_COUNT files" + + # ── Step 5: Upload to Space ──────────────────────────────────────── + echo "[5/6] Uploading to Space..." + cd "$STAGE_DIR" + hf upload "$SPACE_ID" . --type space \ + --commit-message "Deploy: $MODEL, $MAX_TASKS tasks, $MAX_STEPS steps" 2>&1 | tail -3 + echo " ✓ Upload complete" +fi + +# ── Step 6: Monitor ──────────────────────────────────────────────────── +echo "[6/6] Deployment triggered." +echo "" +echo " 🔗 Space: https://huggingface.co/spaces/$SPACE_ID" +echo " 🔗 Logs: https://huggingface.co/spaces/$SPACE_ID?logs=container" +echo "" + +if [ "$MONITOR" = true ]; then + echo "Monitoring build (Ctrl+C to stop)..." + python3 << PYEOF +from huggingface_hub import HfApi +import time, os + +api = HfApi(token=os.environ['HF_TOKEN']) +space_id = "$SPACE_ID" +start = time.time() +last_stage = "" + +while True: + info = api.space_info(space_id) + stage = info.runtime.stage if info.runtime else "UNKNOWN" + elapsed = int(time.time() - start) + + if stage != last_stage: + print(f" [{elapsed:>4}s] {last_stage} → {stage}") + last_stage = stage + + if stage == "RUNNING": + print(f"\n ✅ Space RUNNING after {elapsed}s") + print(f" Health: https://{space_id.replace('/', '-')}.hf.space/health") + break + elif stage in ("RUNTIME_ERROR", "BUILD_ERROR", "CONFIG_ERROR"): + print(f"\n ❌ Failed: {stage}") + print(f" Check logs: https://huggingface.co/spaces/{space_id}?logs=build") + break + + time.sleep(15) +PYEOF +fi + +echo "" +echo "Done. Useful commands:" +echo " # Pause (stop billing):" +echo " bash $0 --pause" +echo " # Resume:" +echo " bash $0 --resume" +echo " # Monitor logs:" +echo " curl -N -H 'Authorization: Bearer \$HF_TOKEN' \\" +echo " 'https://huggingface.co/api/spaces/$SPACE_ID/logs/run'" From 5136337e287e31e07724c3e6a891d6ca72359005 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 20 May 2026 10:48:13 +0530 Subject: [PATCH 51/79] fix: interception params and update max_tokens_cap validation --- envs/coding_agent_env/harness.py | 32 ++++++++++++------- .../server/coding_environment.py | 2 +- tests/envs/test_coding_agent_env.py | 27 ++++++++++++++++ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/envs/coding_agent_env/harness.py b/envs/coding_agent_env/harness.py index 379a055bb..de4ec91dd 100644 --- a/envs/coding_agent_env/harness.py +++ b/envs/coding_agent_env/harness.py @@ -36,6 +36,9 @@ def __init__( task: CodingAgentTask, verifier: Verifier | None = None, base_url_override: str | None = None, + interception_server: InterceptionServer | None = None, + interception_rollout_id: str | None = None, + interception_queue: _queue_mod.Queue[str] | None = None, ) -> None: super().__init__( spec=OPENCODE_SPEC, @@ -44,6 +47,9 @@ def __init__( config=config, verifier=verifier, base_url_override=base_url_override, + interception_server=interception_server, + interception_rollout_id=interception_rollout_id, + interception_queue=interception_queue, ) def fetch_trace(self) -> str: @@ -129,16 +135,21 @@ def create( interception_queue: _queue_mod.Queue[str] | None = None if self._driver.mode == "interception_gate": - assert self._driver._interception_server is not None - assert self._driver._interception_base_url is not None + interception_server = self._driver._interception_server + if interception_server is None: + raise RuntimeError( + "interception_gate mode requires an InterceptionServer" + ) + interception_base_url = self._driver._interception_base_url + if interception_base_url is None: + raise RuntimeError( + "interception_gate mode requires interception_base_url" + ) rollout_id = episode_id or f"rollout_{uuid.uuid4().hex[:8]}" interception_rollout_id = rollout_id - interception_queue = self._driver._interception_server.register_rollout( - rollout_id - ) + interception_queue = interception_server.register_rollout(rollout_id) base_url_override = ( - f"{self._driver._interception_base_url.rstrip('/')}" - f"/rollout/{rollout_id}/v1" + f"{interception_base_url.rstrip('/')}/rollout/{rollout_id}/v1" ) session = CodingAgentSession( @@ -147,11 +158,10 @@ def create( task=oc_task, verifier=self._verifier, base_url_override=base_url_override, + interception_server=self._driver._interception_server, + interception_rollout_id=interception_rollout_id, + interception_queue=interception_queue, ) - # Pass interception fields to the parent CLIAgentSession - session._interception_server = self._driver._interception_server - session._interception_rollout_id = interception_rollout_id - session._interception_queue = interception_queue session.start_agent() return session diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index 9000ed4e0..111d417b8 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -484,7 +484,7 @@ def _build_agent_config( model=model, agent_timeout_s=agent_timeout_s, disable_thinking=disable_thinking, - max_tokens_cap=max_tokens_cap if max_tokens_cap != 4096 else None, + max_tokens_cap=max_tokens_cap if max_tokens_cap > 0 else None, ) provider = self._infer_pi_provider(base_url) diff --git a/tests/envs/test_coding_agent_env.py b/tests/envs/test_coding_agent_env.py index 905713e7a..6397a1060 100644 --- a/tests/envs/test_coding_agent_env.py +++ b/tests/envs/test_coding_agent_env.py @@ -183,6 +183,33 @@ def test_build_agent_config_opencode() -> None: assert isinstance(cfg, env._CodingAgentConfig) assert cfg.model == "gpt-4o-mini" assert cfg.agent_timeout_s == 123.0 + assert cfg.max_tokens_cap == 2048 + + cfg_4096 = env._build_agent_config( + agent="opencode", + mode="black_box", + base_url="https://api.openai.com/v1", + api_key="sk-test", + model="gpt-4o-mini", + agent_timeout_s=123.0, + disable_thinking=True, + top_logprobs=7, + max_tokens_cap=4096, + ) + assert cfg_4096.max_tokens_cap == 4096 + + cfg_uncapped = env._build_agent_config( + agent="opencode", + mode="black_box", + base_url="https://api.openai.com/v1", + api_key="sk-test", + model="gpt-4o-mini", + agent_timeout_s=123.0, + disable_thinking=True, + top_logprobs=7, + max_tokens_cap=0, + ) + assert cfg_uncapped.max_tokens_cap is None def test_build_agent_config_pi() -> None: From 8137b154adb78f305336348ef5735632bb279ecd Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 20 May 2026 10:59:40 +0530 Subject: [PATCH 52/79] refactor: remove RolloutTurn references --- envs/coding_agent_env/README.md | 6 +- envs/coding_agent_env/__init__.py | 3 +- envs/coding_agent_env/models.py | 21 ------ .../server/coding_environment.py | 18 +---- envs/coding_agent_env/server/gradio_ui.py | 70 +++---------------- tests/envs/test_coding_agent_env.py | 15 +--- 6 files changed, 15 insertions(+), 118 deletions(-) diff --git a/envs/coding_agent_env/README.md b/envs/coding_agent_env/README.md index 7825e5c25..347afdd05 100644 --- a/envs/coding_agent_env/README.md +++ b/envs/coding_agent_env/README.md @@ -200,8 +200,8 @@ directly. | `template` | `str` | `""` | E2B template name; `"coding-agent-rl"` skips ~2 min of install per rollout. | Returns `RolloutResult` JSON with: `reward`, `setup_results[]`, -`verify_results[]`, `files{}`, `agent_log_tail`, -`proxy_log_tail`, `wall_s`, `agent_exit_code`, `sandbox_id`, `error`. +`verify_results[]`, `files{}`, `agent_log_tail`, `wall_s`, +`agent_exit_code`, `sandbox_id`, `error`. ## Two Operating Modes @@ -259,7 +259,7 @@ coding_agent_env/ ├── __init__.py # re-exports primitive + client + models │ ├── client.py # CodingAgentEnv(MCPToolClient) -├── models.py # RolloutResult / RolloutTurn / CodingAgentState +├── models.py # RolloutResult / CodingAgentState │ ├── config.py # CodingAgentConfig (primitive) ├── harness.py # CodingAgentSession / CodingAgentSessionFactory (CLI-only) diff --git a/envs/coding_agent_env/__init__.py b/envs/coding_agent_env/__init__.py index 6b839e7ea..bc04e7236 100644 --- a/envs/coding_agent_env/__init__.py +++ b/envs/coding_agent_env/__init__.py @@ -25,7 +25,7 @@ from .client import CodingAgentEnv from .config import CodingAgentConfig, Provider from .harness import CodingAgentSession, CodingAgentSessionFactory -from .models import CommandResult, CodingAgentState, RolloutResult, RolloutTurn +from .models import CommandResult, CodingAgentState, RolloutResult from .task import CodingAgentTask try: @@ -42,7 +42,6 @@ "CommandResult", "CodingAgentState", "RolloutResult", - "RolloutTurn", # Harness primitive "CodingAgentConfig", "CodingAgentSession", diff --git a/envs/coding_agent_env/models.py b/envs/coding_agent_env/models.py index e338a4867..2bf19925e 100644 --- a/envs/coding_agent_env/models.py +++ b/envs/coding_agent_env/models.py @@ -14,26 +14,10 @@ from __future__ import annotations -from typing import Any - from openenv.core.env_server.types import State from pydantic import BaseModel, Field -class RolloutTurn(BaseModel): - """One intercepted LLM turn shape (trainer-owned in interception_gate mode).""" - - turn: int - finish_reason: str | None = None - completion_tokens: list[str] = Field(default_factory=list) - completion_token_ids: list[int] = Field(default_factory=list) - per_token_logps: list[float] = Field(default_factory=list) - latency_s: float = 0.0 - timestamp: float = 0.0 - upstream_status: int | None = None - upstream_error: dict[str, Any] | None = None - - class CommandResult(BaseModel): """Outcome of one bash command in setup/verify. @@ -66,17 +50,12 @@ class RolloutResult(BaseModel): setup_results: list[CommandResult] = Field(default_factory=list) verify_results: list[CommandResult] = Field(default_factory=list) - # Per-turn LLM trajectory placeholder. Capture is trainer-owned in - # interception_gate mode; environment currently leaves this empty. - proxy_turns: list[RolloutTurn] = Field(default_factory=list) - # Filesystem the agent produced (path -> contents, truncated) files: dict[str, str] = Field(default_factory=dict) files_extra: list[str] = Field(default_factory=list) # Diagnostic tails agent_log_tail: str = "" - proxy_log_tail: str = "" # Error surfacing error: str | None = None diff --git a/envs/coding_agent_env/server/coding_environment.py b/envs/coding_agent_env/server/coding_environment.py index 111d417b8..9174666e7 100644 --- a/envs/coding_agent_env/server/coding_environment.py +++ b/envs/coding_agent_env/server/coding_environment.py @@ -54,7 +54,6 @@ _log = logging.getLogger(__name__) REWARD_FILE = f"{HOME}/logs/verifier/reward.txt" -PROXY_LOG = f"{HOME}/logs/agent/proxy.log" AGENT_LOG = f"{HOME}/logs/agent/opencode.jsonl" VERIFY_TIMEOUT_S = 120 _SUPPORTED_AGENTS = ("opencode", "pi") @@ -89,14 +88,12 @@ def __init__(self) -> None: CodingAgentState, CommandResult, RolloutResult, - RolloutTurn, ) except ImportError: # pragma: no cover from models import ( # type: ignore CodingAgentState, CommandResult, RolloutResult, - RolloutTurn, ) from openenv.core.harness.agents import get_agent_spec @@ -113,7 +110,6 @@ def __init__(self) -> None: self._CommandResult = CommandResult self._RolloutResult = RolloutResult - self._RolloutTurn = RolloutTurn self._CodingAgentState = CodingAgentState self._CodingAgentConfig = CodingAgentConfig self._CodingAgentSessionFactory = CodingAgentSessionFactory @@ -418,24 +414,18 @@ def _emit(msg: str) -> None: else: result.reward = None - # Collect filesystem + proxy trace. - _emit("collecting workdir files + proxy trace + logs") + # Collect filesystem + agent log tail. + _emit("collecting workdir files + logs") result.files, result.files_extra = self._collect_files(session.sandbox) - result.proxy_turns = self._collect_proxy_turns(session) - result.proxy_log_tail = self._safe_read(session.sandbox, PROXY_LOG)[-2000:] result.agent_log_tail = self._collect_agent_log_tail(session, agent) _emit( f"collected: {len(result.files)} file(s), " - f"{len(result.proxy_turns)} proxy turn(s), " f"reward={'%.2f' % result.reward if result.reward is not None else 'n/a'}" ) except Exception as exc: # noqa: BLE001 result.error = f"{type(exc).__name__}: {exc}" _emit(f"ERROR: {result.error}") if session is not None: - result.proxy_log_tail = self._safe_read(session.sandbox, PROXY_LOG)[ - -2000: - ] result.agent_log_tail = self._collect_agent_log_tail(session, agent) finally: if session is not None: @@ -607,10 +597,6 @@ def _collect_files(self, sandbox: Any) -> tuple[dict[str, str], list[str]]: extras.append(path) return files, extras - def _collect_proxy_turns(self, session: Any) -> list[Any]: - """Logprob capture is now owned by the training loop via interception_gate.""" - return [] - @staticmethod def _safe_read(sandbox: Any, path: str) -> str: try: diff --git a/envs/coding_agent_env/server/gradio_ui.py b/envs/coding_agent_env/server/gradio_ui.py index 82f130ce3..ea9cdb81f 100644 --- a/envs/coding_agent_env/server/gradio_ui.py +++ b/envs/coding_agent_env/server/gradio_ui.py @@ -19,8 +19,7 @@ agent_timeout_s, template). - Preset buttons for the ready-made example tasks. - Run button → result panel with reward, setup/verify per-command - results, file outputs, logprob stats, agent + proxy log tails, - and the raw RolloutResult JSON. + results, file outputs, agent log tail, and the raw RolloutResult JSON. """ from __future__ import annotations @@ -156,51 +155,6 @@ def _command_rows(items: list[dict[str, Any]]) -> list[list[str]]: return rows -def _logprobs_md(turns: list[dict[str, Any]]) -> str: - if not turns: - return "_No proxy turns captured._\n\nLogprob capture is handled by the training loop via `interception_gate` mode." - n = len(turns) - productive = sum(1 for t in turns if t.get("completion_tokens")) - total_toks = sum(len(t.get("completion_tokens") or []) for t in turns) - all_lps = [ - float(x) - for t in turns - for x in (t.get("per_token_logps") or []) - if x is not None - ] - mean_lp = (sum(all_lps) / len(all_lps)) if all_lps else None - lines = [ - f"**turns**: `{n}` · **productive**: `{productive}` · " - f"**total_completion_tokens**: `{total_toks}`", - ] - if mean_lp is not None: - lines.append(f"**mean_logprob**: `{mean_lp:+.4f}`") - finishes: dict[str, int] = {} - for t in turns: - f = t.get("finish_reason") or "unknown" - finishes[f] = finishes.get(f, 0) + 1 - if finishes: - lines.append( - "**finish_reasons**: " - + " ".join(f"`{k}={v}`" for k, v in finishes.items()) - ) - productive_rows = [t for t in turns if t.get("completion_tokens")] - if productive_rows: - first = productive_rows[0] - toks = first["completion_tokens"][:10] - lps = first.get("per_token_logps") or [] - lines.append( - "\n**first productive turn (first 10 tokens)**\n\n" - "```\n" - + "\n".join( - f" {tok!r:<14} {lp:+.3f}" if i < len(lps) else f" {tok!r:<14} -" - for i, (tok, lp) in enumerate(zip(toks, lps + [None] * len(toks))) - ) - + "\n```" - ) - return "\n\n".join(lines) - - def _live_status_md( agent: str, endpoint_kind: str, @@ -292,9 +246,9 @@ def run( """Generator handler — yields incremental UI updates. Each ``yield`` is a tuple matching ``outputs=[...]``: - (summary_md, setup_table, verify_table, files_md, logprobs_md, - logs_md, raw_json). Early yields keep summary_md as a live phase - log while the rollout runs; the final yield populates everything. + (summary_md, setup_table, verify_table, files_md, logs_md, + raw_json). Early yields keep summary_md as a live phase log while + the rollout runs; the final yield populates everything. """ import queue import threading @@ -308,7 +262,7 @@ def run( ) except ValueError as exc: err = f"endpoint resolution failed: {exc}" - yield (f"### error\n\n```\n{err}\n```", [], [], "", "", "", {"error": err}) + yield (f"### error\n\n```\n{err}\n```", [], [], "", "", {"error": err}) return # Translate "auto" / "on" / "off" into bool / None. @@ -369,7 +323,6 @@ def _worker(): [], "", "", - "", {}, ) @@ -397,7 +350,7 @@ def _worker(): elapsed, status_lines, ) - yield (md, [], [], "", "", "", {}) + yield (md, [], [], "", "", {}) # Drain any final messages still in the queue. while not status_q.empty(): @@ -415,7 +368,6 @@ def _worker(): [], [], "", - "", _live_status_md( agent, resolved.kind, @@ -434,7 +386,6 @@ def _worker(): _command_rows(result.get("setup_results") or []), _command_rows(result.get("verify_results") or []), _files_md(result.get("files") or {}), - _logprobs_md(result.get("proxy_turns") or []), ( "### live phase log\n\n" + _live_status_md( @@ -445,8 +396,7 @@ def _worker(): time.time() - t_start, status_lines, ) - + f"\n\n### agent log (tail)\n```\n{result.get('agent_log_tail', '')[:4000]}\n```\n\n" - f"### proxy log (tail)\n```\n{result.get('proxy_log_tail', '')[:4000]}\n```" + + f"\n\n### agent log (tail)\n```\n{result.get('agent_log_tail', '')[:4000]}\n```" ), result, ) @@ -460,8 +410,7 @@ def apply_preset(name: str) -> tuple[str, str, str]: gr.Markdown( "Run one coding-agent rollout in an E2B sandbox against your chosen " "LLM endpoint. Pick an agent + endpoint, write the task as " - "`(instruction, setup, verify)`, and inspect reward + per-token " - "logprobs." + "`(instruction, setup, verify)`, and inspect reward + logs." ) gr.Markdown(_catalog_banner()) @@ -563,8 +512,6 @@ def apply_preset(name: str) -> tuple[str, str, str]: ) with gr.Tab("Files"): files_md = gr.Markdown("") - with gr.Tab("Logprobs"): - logprobs_md = gr.Markdown("") with gr.Tab("Logs"): logs_md = gr.Markdown("") with gr.Tab("Raw JSON"): @@ -604,7 +551,6 @@ def apply_preset(name: str) -> tuple[str, str, str]: setup_table, verify_table, files_md, - logprobs_md, logs_md, raw_json, ], diff --git a/tests/envs/test_coding_agent_env.py b/tests/envs/test_coding_agent_env.py index 6397a1060..fa3dcae79 100644 --- a/tests/envs/test_coding_agent_env.py +++ b/tests/envs/test_coding_agent_env.py @@ -55,7 +55,6 @@ def test_public_api_imports() -> None: E2BSandboxBackend, Provider, RolloutResult, - RolloutTurn, SandboxBackend, SandboxHandle, ) @@ -280,7 +279,7 @@ def test_build_session_factory_requires_e2b_dependency() -> None: def test_rollout_result_serializes_round_trip() -> None: - from coding_agent_env import CommandResult, RolloutResult, RolloutTurn + from coding_agent_env import CommandResult, RolloutResult r = RolloutResult( task_id="t1", @@ -291,22 +290,12 @@ def test_rollout_result_serializes_round_trip() -> None: mode="black_box", setup_results=[CommandResult(cmd="pip install pandas", exit_code=0)], verify_results=[CommandResult(cmd="pytest", exit_code=1, stderr="boom")], - proxy_turns=[ - RolloutTurn( - turn=1, - finish_reason="stop", - completion_tokens=["hi"], - per_token_logps=[-0.1], - latency_s=0.2, - ) - ], files={"/home/user/workdir/x.py": "print('x')"}, ) blob = r.model_dump_json() rebuilt = RolloutResult.model_validate_json(blob) assert rebuilt.reward == 0.75 assert rebuilt.verify_results[0].exit_code == 1 - assert rebuilt.proxy_turns[0].completion_tokens == ["hi"] def test_coding_agent_task_coerce_str() -> None: @@ -402,8 +391,6 @@ async def _go() -> RolloutResult: assert result.reward == 1.0, ( f"expected reward=1.0 got {result.reward}: {result.error}" ) - # proxy_turns is now always empty — logprob capture is trainer-owned - # via interception_gate mode, not captured by the environment. assert any(f.endswith("/binary_search.py") for f in result.files), ( f"expected binary_search.py in workdir, got {list(result.files)}" ) From 3c4ffa4a6a0fb3d120e136936d3627726193c4ba Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 20 May 2026 11:17:11 +0530 Subject: [PATCH 53/79] feat: add tool name allowlist validation --- .../harness/agents/interception_server.py | 70 +++++++++++++++++- tests/core/test_interception_server.py | 71 ++++++++++++++++++- 2 files changed, 136 insertions(+), 5 deletions(-) diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index a71082e69..fa735f0c0 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -24,7 +24,7 @@ Usage — training loop:: - server = InterceptionServer(port=8765) + server = InterceptionServer(port=8765, tool_name_allowlist={"answer"}) await server.start() # Make the server reachable — your responsibility. @@ -53,12 +53,15 @@ import json import logging import queue as _queue_mod +import re import secrets import threading import time import uuid from typing import Any, Awaitable, Callable +from openenv.core.env_server.mcp_types import RESERVED_TOOL_NAMES + from aiohttp import web @@ -66,6 +69,7 @@ _KEEPALIVE_INTERVAL_S = 3.0 _MAX_REQUEST_BODY = 16 * 1024 * 1024 +_TOOL_NAME_RE = re.compile(r"^[A-Za-z0-9_-]{1,64}$") ToolHandler = Callable[[dict[str, Any]], Awaitable[dict[str, Any]]] @@ -82,12 +86,25 @@ def __init__( port: int = 0, secret: str | None = None, host: str = "127.0.0.1", + tool_name_allowlist: set[str] | None = None, ) -> None: self.port = port self.host = host self.secret = secret or secrets.token_urlsafe(32) if not self.secret.strip(): raise ValueError("InterceptionServer secret must not be blank.") + normalized_allowlist: set[str] = set() + for raw_name in tool_name_allowlist or set(): + name = raw_name.strip() + if not name: + raise ValueError("tool_name_allowlist must not include blank names") + if not _TOOL_NAME_RE.fullmatch(name): + raise ValueError( + "tool_name_allowlist entries must match " + f"^[A-Za-z0-9_-]{{1,64}}$ (got {raw_name!r})" + ) + normalized_allowlist.add(name) + self._tool_name_allowlist = frozenset(normalized_allowlist) self._app: web.Application | None = None self._runner: web.AppRunner | None = None self._site: web.TCPSite | None = None @@ -237,16 +254,25 @@ def register_tool_handler( Optionally provide ``tool_definition`` (OpenAI tool schema). Registered schemas are injected into intercepted chat-completion requests for the rollout when the incoming request does not already include the tool. + + Only tool names explicitly configured in ``tool_name_allowlist`` are + accepted. Control-plane names (``reset``, ``step``, ``state``, + ``close``) are always rejected to preserve the dual API boundary. """ + normalized_name = self._validate_tool_registration( + tool_name, + tool_definition=tool_definition, + ) + with self._state_lock: context = self.active_rollouts.get(rollout_id) if context is None: raise KeyError(f"rollout not found: {rollout_id}") handlers: dict[str, ToolHandler] = context["tool_handlers"] - handlers[tool_name] = handler + handlers[normalized_name] = handler if tool_definition is not None: tool_defs: dict[str, dict[str, Any]] = context["tool_defs"] - tool_defs[tool_name] = tool_definition + tool_defs[normalized_name] = tool_definition def unregister_tool_handler(self, rollout_id: str, tool_name: str) -> None: with self._state_lock: @@ -268,6 +294,44 @@ def _tool_name(tool: dict[str, Any]) -> str | None: name = function.get("name") return name if isinstance(name, str) and name else None + def _validate_tool_registration( + self, + tool_name: str, + *, + tool_definition: dict[str, Any] | None, + ) -> str: + normalized = tool_name.strip() + if not normalized: + raise ValueError("tool_name must not be blank") + if not _TOOL_NAME_RE.fullmatch(normalized): + raise ValueError( + f"tool_name must match ^[A-Za-z0-9_-]{{1,64}}$ (got {tool_name!r})" + ) + if normalized.lower() in RESERVED_TOOL_NAMES: + raise ValueError( + "Interception tool name is reserved for infrastructure/control " + f"APIs: {normalized!r}" + ) + if normalized not in self._tool_name_allowlist: + raise ValueError( + "Interception tool name is not in the configured allowlist: " + f"{normalized!r}" + ) + + if tool_definition is not None: + definition_name = self._tool_name(tool_definition) + if definition_name is None: + raise ValueError( + "tool_definition must be an OpenAI tool schema with function.name" + ) + if definition_name != normalized: + raise ValueError( + "tool_definition.function.name must exactly match tool_name " + f"({definition_name!r} != {normalized!r})" + ) + + return normalized + def _merge_rollout_tools( self, tools: Any, diff --git a/tests/core/test_interception_server.py b/tests/core/test_interception_server.py index 73421e1a7..41ef38fe5 100644 --- a/tests/core/test_interception_server.py +++ b/tests/core/test_interception_server.py @@ -142,7 +142,11 @@ async def test_interception_server_unregister_rollout_cancels_pending_request() @pytest.mark.asyncio async def test_interception_server_tool_endpoint_executes_registered_handler() -> None: - server = InterceptionServer(port=0, secret="secret-token") + server = InterceptionServer( + port=0, + secret="secret-token", + tool_name_allowlist={"answer"}, + ) await server.start() server.register_rollout("r1") seen: dict[str, object] = {} @@ -186,11 +190,74 @@ async def test_interception_server_tool_endpoint_returns_404_for_unknown_tool() await server.stop() +def test_interception_server_rejects_reserved_tool_name_registration() -> None: + server = InterceptionServer( + port=0, + secret="secret-token", + tool_name_allowlist={"reset"}, + ) + server.register_rollout("r1") + + async def _handler(arguments: dict) -> dict: + return {"ok": True} + + with pytest.raises(ValueError, match="reserved"): + server.register_tool_handler("r1", "reset", _handler) + + +def test_interception_server_rejects_tool_definition_name_mismatch() -> None: + server = InterceptionServer( + port=0, + secret="secret-token", + tool_name_allowlist={"answer"}, + ) + server.register_rollout("r1") + + async def _handler(arguments: dict) -> dict: + return {"ok": True} + + mismatched = { + "type": "function", + "function": { + "name": "not_answer", + "description": "Mismatch", + "parameters": {"type": "object", "properties": {}}, + }, + } + + with pytest.raises(ValueError, match="must exactly match"): + server.register_tool_handler( + "r1", + "answer", + _handler, + tool_definition=mismatched, + ) + + +def test_interception_server_rejects_tool_not_in_allowlist() -> None: + server = InterceptionServer( + port=0, + secret="secret-token", + tool_name_allowlist={"answer"}, + ) + server.register_rollout("r1") + + async def _handler(arguments: dict) -> dict: + return {"ok": True} + + with pytest.raises(ValueError, match="allowlist"): + server.register_tool_handler("r1", "search", _handler) + + @pytest.mark.asyncio async def test_interception_server_injects_registered_tool_defs_into_intercept() -> ( None ): - server = InterceptionServer(port=0, secret="secret-token") + server = InterceptionServer( + port=0, + secret="secret-token", + tool_name_allowlist={"answer"}, + ) await server.start() queue = server.register_rollout("r1") From 151d1abc9d73e0508f008149337704b986a335ec Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 20 May 2026 11:23:27 +0530 Subject: [PATCH 54/79] feat: provider-specific env var handling for Pi agent --- src/openenv/core/harness/agents/pi.py | 56 ++++++++++++++++++++++----- tests/core/test_harness_adapters.py | 38 ++++++++++++++++-- 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py index 03946c552..060b41dbd 100644 --- a/src/openenv/core/harness/agents/pi.py +++ b/src/openenv/core/harness/agents/pi.py @@ -11,8 +11,9 @@ pi --no-session --no-context-files --provider

--model --thinking off \\ -p @/home/user/task/instruction.txt 2>&1 | tee /home/user/logs/agent/pi.txt -The provider and model are passed as CLI flags so the spec's ``env`` dict -only needs auth credentials (``HF_TOKEN``, ``OPENAI_API_KEY``, etc.). +The provider and model are passed as CLI flags. Provider-specific credentials +are exported via ``build_env_vars`` according to Pi's provider docs +(``HF_TOKEN`` for ``huggingface``, ``OPENAI_API_KEY`` for ``openai``, etc.). Registered on import:: @@ -111,6 +112,48 @@ def _parse_events(line: str) -> AgentEvent | None: return AgentEvent(type="assistant", data=data, raw=line) +def _provider_api_key_env(provider: str) -> str: + provider_key = provider.strip().lower() + env_by_provider = { + # https://github.com/earendil-works/pi/tree/main/packages/coding-agent#providers--models + "openai": "OPENAI_API_KEY", + "openenv": "OPENAI_API_KEY", + "huggingface": "HF_TOKEN", + "anthropic": "ANTHROPIC_API_KEY", + "gemini": "GEMINI_API_KEY", + "google": "GEMINI_API_KEY", + } + env_name = env_by_provider.get(provider_key) + if env_name is None: + raise ValueError( + f"Unsupported pi provider {provider!r}; expected one of " + f"{sorted(env_by_provider)}" + ) + return env_name + + +def _build_env_vars(spec: CLIAgentSpec, config: Any) -> dict[str, str]: + provider = config.provider if hasattr(config, "provider") else "openai" + if not isinstance(provider, str) or not provider.strip(): + provider = "openai" + api_key = config.api_key if hasattr(config, "api_key") else "" + base_url = config.base_url if hasattr(config, "base_url") else "" + extra_env = config.extra_env if hasattr(config, "extra_env") else {} + + env = dict(extra_env) + env["PI_SKIP_VERSION_CHECK"] = "1" + env["PI_TELEMETRY"] = "0" + + if base_url: + env["OPENAI_BASE_URL"] = base_url + + key_env_var = _provider_api_key_env(provider) + if api_key: + env[key_env_var] = api_key + + return env + + PI_SPEC = CLIAgentSpec( name="pi", install_check_cmd=["pi", "--version"], @@ -137,17 +180,12 @@ def _parse_events(line: str) -> AgentEvent | None: artifacts={ "agent_log": ArtifactSpec(path="/home/user/logs/agent/pi.txt"), }, - env={ - "HF_TOKEN": "{api_key}", - "OPENAI_API_KEY": "{api_key}", - "OPENAI_BASE_URL": "{base_url}", - "PI_SKIP_VERSION_CHECK": "1", - "PI_TELEMETRY": "0", - }, + env=None, extension_dir_template="{home}/.pi/agent/extensions", build_command=_build_command, build_mcp_config=_build_mcp_config, parse_events=_parse_events, + build_env_vars=_build_env_vars, ) register_agent(PI_SPEC) diff --git a/tests/core/test_harness_adapters.py b/tests/core/test_harness_adapters.py index f5e1dc260..1766b8ad4 100644 --- a/tests/core/test_harness_adapters.py +++ b/tests/core/test_harness_adapters.py @@ -47,9 +47,41 @@ def test_fields(self): assert PI_SPEC.mcp_config.method == "config_file" assert PI_SPEC.mcp_config.path_template is not None assert ".mcp.json" in PI_SPEC.mcp_config.path_template - assert PI_SPEC.env is not None - assert "HF_TOKEN" in PI_SPEC.env - assert "PI_SKIP_VERSION_CHECK" in PI_SPEC.env + assert PI_SPEC.build_env_vars is not None + + def test_build_env_vars_provider_specific_api_key(self): + from openenv.core.harness.agents.pi import PI_SPEC + + @dataclass + class PiConfig: + provider: str + api_key: str = "secret" + base_url: str = "https://api.example.com/v1" + extra_env: dict[str, str] = field(default_factory=dict) + + assert PI_SPEC.build_env_vars is not None + + hf_env = PI_SPEC.build_env_vars(PI_SPEC, PiConfig(provider="huggingface")) + assert hf_env["HF_TOKEN"] == "secret" + assert "OPENAI_API_KEY" not in hf_env + + oa_env = PI_SPEC.build_env_vars(PI_SPEC, PiConfig(provider="openai")) + assert oa_env["OPENAI_API_KEY"] == "secret" + assert "HF_TOKEN" not in oa_env + + def test_build_env_vars_rejects_unknown_provider(self): + from openenv.core.harness.agents.pi import PI_SPEC + + @dataclass + class PiConfig: + provider: str = "unknown" + api_key: str = "secret" + base_url: str = "https://api.example.com/v1" + extra_env: dict[str, str] = field(default_factory=dict) + + assert PI_SPEC.build_env_vars is not None + with pytest.raises(ValueError, match="Unsupported pi provider"): + PI_SPEC.build_env_vars(PI_SPEC, PiConfig()) def test_build_command(self): from openenv.core.harness.agents.pi import PI_SPEC From 73bb3a69acdbec991af69e887c548165dabd2709 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 20 May 2026 11:47:58 +0530 Subject: [PATCH 55/79] feat: tool name allowlist to InterceptionServer init --- envs/mini_swe_env/async_grpo/control_plane.py | 1 + examples/mini_swe_env/run_swe_sample.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/envs/mini_swe_env/async_grpo/control_plane.py b/envs/mini_swe_env/async_grpo/control_plane.py index ecd425b99..719404426 100644 --- a/envs/mini_swe_env/async_grpo/control_plane.py +++ b/envs/mini_swe_env/async_grpo/control_plane.py @@ -103,6 +103,7 @@ def __init__( host=config.host, port=config.port, secret=config.auth_token, + tool_name_allowlist={"answer"}, ) @property diff --git a/examples/mini_swe_env/run_swe_sample.py b/examples/mini_swe_env/run_swe_sample.py index e9cd10d52..64ce7b66f 100644 --- a/examples/mini_swe_env/run_swe_sample.py +++ b/examples/mini_swe_env/run_swe_sample.py @@ -143,7 +143,11 @@ async def _run(args: argparse.Namespace) -> int: gym_task = tasks[args.task_index] swe_task = gym_task.to_swe_task() - server = InterceptionServer(port=args.interception_port, host=args.interception_host) + server = InterceptionServer( + port=args.interception_port, + host=args.interception_host, + tool_name_allowlist={"answer"}, + ) await server.start() try: From cf15c8b5f5b28516d7d4bae34cbef568c7b71cb7 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Wed, 20 May 2026 12:20:17 +0530 Subject: [PATCH 56/79] refactor(mini_swe): move async_grpo training stack from env package to examples --- envs/mini_swe_env/pyproject.toml | 15 +- envs/mini_swe_env/uv.lock | 669 +----------------- examples/mini_swe_env/README.md | 2 +- .../mini_swe_env/async_grpo/__init__.py | 0 .../mini_swe_env/async_grpo/control_plane.py | 0 .../mini_swe_env/async_grpo/rollout_worker.py | 2 +- .../async_grpo/space_app/Dockerfile | 0 .../async_grpo/space_app/README.md | 0 .../async_grpo/space_app/deploy_hf_space.sh | 9 +- .../async_grpo/space_app/start.sh | 0 examples/mini_swe_env/train_swe_async_grpo.py | 40 +- tests/envs/test_swe_async_rollout_worker.py | 9 +- 12 files changed, 44 insertions(+), 702 deletions(-) rename {envs => examples}/mini_swe_env/async_grpo/__init__.py (100%) rename {envs => examples}/mini_swe_env/async_grpo/control_plane.py (100%) rename {envs => examples}/mini_swe_env/async_grpo/rollout_worker.py (99%) rename {envs => examples}/mini_swe_env/async_grpo/space_app/Dockerfile (100%) rename {envs => examples}/mini_swe_env/async_grpo/space_app/README.md (100%) rename {envs => examples}/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh (97%) rename {envs => examples}/mini_swe_env/async_grpo/space_app/start.sh (100%) diff --git a/envs/mini_swe_env/pyproject.toml b/envs/mini_swe_env/pyproject.toml index 3a641b505..b093fd303 100644 --- a/envs/mini_swe_env/pyproject.toml +++ b/envs/mini_swe_env/pyproject.toml @@ -11,7 +11,7 @@ build-backend = "setuptools.build_meta" [project] name = "openenv-mini-swe-env" version = "0.1.0" -description = "Mini SWE environment for OpenEnv — SWE-Gym task training with Pi agent and Async GRPO." +description = "Mini SWE environment for OpenEnv — SWE-Gym task execution and host-side grading." requires-python = ">=3.10" dependencies = [ # Core OpenEnv (server + MCP). 0.3.0 ships the harness runtime. @@ -23,18 +23,9 @@ dependencies = [ "requests>=2.25.0", "aiohttp>=3.13.5", "datasets>=4.8.0", - "bitsandbytes>=0.43.0", ] [project.optional-dependencies] -train = [ - "trl>=1.4.0", - "accelerate>=1.13.0", - "hf-sandbox>=0.1.1", - "datasets>=4.8.0", - "aiohttp>=3.13.5", - "fastmcp>=3.3.0", -] dev = ["pytest>=8.0.0", "pytest-asyncio>=0.23.0", "pytest-cov>=4.0.0"] [project.scripts] @@ -42,5 +33,5 @@ server = "mini_swe_env.server.app:main" [tool.setuptools] include-package-data = true -packages = ["mini_swe_env", "mini_swe_env.server", "mini_swe_env.async_grpo"] -package-dir = { "mini_swe_env" = ".", "mini_swe_env.server" = "server", "mini_swe_env.async_grpo" = "async_grpo" } +packages = ["mini_swe_env", "mini_swe_env.server"] +package-dir = { "mini_swe_env" = ".", "mini_swe_env.server" = "server" } diff --git a/envs/mini_swe_env/uv.lock b/envs/mini_swe_env/uv.lock index 15ed94544..efbdfbd75 100644 --- a/envs/mini_swe_env/uv.lock +++ b/envs/mini_swe_env/uv.lock @@ -14,25 +14,6 @@ resolution-markers = [ "python_full_version < '3.11'", ] -[[package]] -name = "accelerate" -version = "1.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pyyaml" }, - { name = "safetensors" }, - { name = "torch" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, -] - [[package]] name = "aiofile" version = "3.9.0" @@ -850,76 +831,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, ] -[[package]] -name = "cuda-bindings" -version = "13.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, - { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, - { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, - { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, - { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, - { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, - { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, - { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/d0/c177e29701cf1d3008d7d2b16b5fc626592ce13bd535f8795c5f57187e0e/cuda_pathfinder-1.5.4-py3-none-any.whl", hash = "sha256:9563d3175ce1828531acf4b94e1c1c7d67208c347ca002493e2654878b26f4b7", size = 51657, upload-time = "2026-04-27T22:42:07.712Z" }, -] - -[[package]] -name = "cuda-toolkit" -version = "13.0.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, -] - -[package.optional-dependencies] -cudart = [ - { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cufft = [ - { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cufile = [ - { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, -] -cupti = [ - { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -curand = [ - { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cusolver = [ - { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cusparse = [ - { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvtx = [ - { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] - [[package]] name = "cyclopts" version = "4.13.0" @@ -1351,20 +1262,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/2d/afff2ee87e75d8eb85c92bb8cf0e15b05c23c2ebd8fd8dec781d8601ed7f/hf_gradio-0.4.1-py3-none-any.whl", hash = "sha256:76b8cb8be6abe62d74c1ad2d35b42f0629db89aa9e1a8d033cecfe7c856eeab3", size = 4482, upload-time = "2026-04-17T19:53:31.827Z" }, ] -[[package]] -name = "hf-sandbox" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dnspython" }, - { name = "httpx" }, - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/b2/8975aafebf0deb9e137b59c141698dc0e911c68543ca84fd78fc3ffd2edf/hf_sandbox-0.1.1.tar.gz", hash = "sha256:4f4af45a9e0fef33e1d537204b21ce6c09f9f0347d65526be794be81b6987513", size = 5654, upload-time = "2026-05-10T15:48:38.276Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/aa/292aa188bbd16b68a13cbd5229e17ba07ca487ef65290995f337c6935a99/hf_sandbox-0.1.1-py3-none-any.whl", hash = "sha256:3200c89521854486945494b765f5106107cca0a4f3247b01f520e8d657e17446", size = 4906, upload-time = "2026-05-10T15:48:36.592Z" }, -] - [[package]] name = "hf-xet" version = "1.5.0" @@ -1907,15 +1804,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, ] -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - [[package]] name = "multidict" version = "6.7.1" @@ -2077,38 +1965,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" }, ] -[[package]] -name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, -] - -[[package]] -name = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, -] - [[package]] name = "numpy" version = "2.2.6" @@ -2264,158 +2120,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/dc/d358a16a6fec86cf736b8fbe67386044b3fa2aded1a80cff90e836799301/numpy-2.4.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:40c71d50a4da1a7c317af419461052d3911a5770bfc5fd55baf52cc45e7a2c20", size = 12504085, upload-time = "2026-05-15T20:25:16.667Z" }, ] -[[package]] -name = "nvidia-cublas" -version = "13.1.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cuda-nvrtc" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti" -version = "13.0.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, - { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc" -version = "13.0.88" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, - { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime" -version = "13.0.96" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, - { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu13" -version = "9.20.0.48" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, - { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" }, -] - -[[package]] -name = "nvidia-cufft" -version = "12.0.0.61" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, -] - -[[package]] -name = "nvidia-cufile" -version = "1.15.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, - { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, -] - -[[package]] -name = "nvidia-curand" -version = "10.4.0.35" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, - { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, -] - -[[package]] -name = "nvidia-cusolver" -version = "12.0.4.66" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas" }, - { name = "nvidia-cusparse" }, - { name = "nvidia-nvjitlink" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, - { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, -] - -[[package]] -name = "nvidia-cusparse" -version = "12.6.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, - { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu13" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" }, - { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" }, -] - -[[package]] -name = "nvidia-nccl-cu13" -version = "2.29.7" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" }, - { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" }, -] - -[[package]] -name = "nvidia-nvjitlink" -version = "13.0.88" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu13" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, - { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, -] - -[[package]] -name = "nvidia-nvtx" -version = "13.0.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, - { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, -] - [[package]] name = "openai" version = "2.37.0" @@ -2503,36 +2207,22 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, ] -train = [ - { name = "accelerate" }, - { name = "hf-sandbox" }, - { name = "torch" }, - { name = "trackio" }, - { name = "transformers" }, - { name = "trl" }, -] [package.metadata] requires-dist = [ - { name = "accelerate", marker = "extra == 'train'", specifier = ">=1.13.0" }, { name = "aiohttp", specifier = ">=3.13.5" }, { name = "datasets", specifier = ">=4.8.0" }, { name = "fastapi", specifier = ">=0.104.0" }, { name = "fastmcp", specifier = ">=3.3.0" }, - { name = "hf-sandbox", marker = "extra == 'train'", specifier = ">=0.1.1" }, { name = "openenv-core", extras = ["core"], specifier = ">=0.3.0" }, { name = "pydantic", specifier = ">=2.0.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, { name = "requests", specifier = ">=2.25.0" }, - { name = "torch", marker = "extra == 'train'", specifier = ">=2.6.0" }, - { name = "trackio", marker = "extra == 'train'", specifier = ">=0.25.0" }, - { name = "transformers", marker = "extra == 'train'", specifier = ">=5.8.0" }, - { name = "trl", marker = "extra == 'train'", specifier = ">=1.4.0" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0" }, ] -provides-extras = ["train", "dev"] +provides-extras = ["dev"] [[package]] name = "opentelemetry-api" @@ -3025,34 +2715,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, ] -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, - { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, - { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, -] - [[package]] name = "py-key-value-aio" version = "0.4.4" @@ -3533,127 +3195,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] -[[package]] -name = "regex" -version = "2026.5.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ed/0ad2c8edf634918eb4484365d3819fa7bd7f58daf807fe7fb21812c316e5/regex-2026.5.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a9e1328e17c84c1a5d22ec9f785ecef4a967fab9a42b6a8dc3bcbebd0a0c9e44", size = 489438, upload-time = "2026-05-09T23:11:29.374Z" }, - { url = "https://files.pythonhosted.org/packages/89/a9/4ed972ad263963b860b7c3e86e0e1bcc791def47b43b8c8efe57e710f139/regex-2026.5.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfe1ce50cbfb569d74e1e4337da6468961f31dbea55fd85aa5de59c0947a805a", size = 291270, upload-time = "2026-05-09T23:11:33.254Z" }, - { url = "https://files.pythonhosted.org/packages/16/81/075930d9fa28c4ea1f53398dd015ee7c882f623539759113cda1257f4b82/regex-2026.5.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15ee42209947f4ca045412eae98416317238163618ace2a8e54f99586a466733", size = 289198, upload-time = "2026-05-09T23:11:35.769Z" }, - { url = "https://files.pythonhosted.org/packages/d4/c8/5cdfbf0b5dc6599e1b6131eff43262e5275d4ec3469ce10216061659aadb/regex-2026.5.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4bb445ff3f725f59df8f6014edb547ee928ec7023a774f6a39a3f953038cbb2", size = 784765, upload-time = "2026-05-09T23:11:37.689Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ca/ae5fd6edc59b7f84b904b31d6ec39a860cbcecd10f64bd5a062ca83a4864/regex-2026.5.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:446ddd671e43ab535810c4b21cff7104945c701d4a14d1e6d1cd6f4e445a8bea", size = 852115, upload-time = "2026-05-09T23:11:39.973Z" }, - { url = "https://files.pythonhosted.org/packages/f6/ce/a91cf555afb51f3b74a182e24ba073b91ea7bb64592fc4b315c111bb19fd/regex-2026.5.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b92817338591505f282cf3864c145244b1edcf5381d237038df955001091538", size = 899503, upload-time = "2026-05-09T23:11:42.48Z" }, - { url = "https://files.pythonhosted.org/packages/55/7f/725a0a2b245a4cf0c4bab29d0e97c74285d94136a65d1b55a6459a583502/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b8a143aca6c39b446ea8092cde25cc8fe9304d4f5fecfbc1a9dbb0282703c2", size = 794093, upload-time = "2026-05-09T23:11:44.681Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2a/996efbd59ce6b5d4a09e3af6180ceb62af171f4a9a6fb557d2f0ae0d462b/regex-2026.5.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0f03aa6898aaaac4592479821df16e68e8d0e29e903e65d8f2dfb2f19028a989", size = 786234, upload-time = "2026-05-09T23:11:46.882Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0a/8731e8b8806174c9cdd5903f80a14990331c1f42fc4209b540952e9e010d/regex-2026.5.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ed457d8e98ae812ed7732bef7bf78de78e834eae0372a74e23ca90ef21d910f9", size = 769895, upload-time = "2026-05-09T23:11:49.324Z" }, - { url = "https://files.pythonhosted.org/packages/9a/0b/932473194bd563f342a412ae2ffbbd6da608306a2bc4e99249a41c2b0b92/regex-2026.5.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71b61c5bfe1c806332defc42ad6c780b3c55f661986d7f40283a3a88274b4c00", size = 774991, upload-time = "2026-05-09T23:11:51.261Z" }, - { url = "https://files.pythonhosted.org/packages/98/80/9523d196010031df25f7177ee0a467efbee436324038e5d99def17a57515/regex-2026.5.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3b1e39888c5e0c7d92cea4fc777396c4a90363b05de75d02eb459a4752200808", size = 848790, upload-time = "2026-05-09T23:11:53.232Z" }, - { url = "https://files.pythonhosted.org/packages/3c/07/56987b35e89edf47e4a38cf2845aeee476bfa688a6bdbd3e820cda461dc1/regex-2026.5.9-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6ba42b2e7e7f46cf68cc6a5ca36fa07959f9bbd9c6bdcc47b6ee76549a590248", size = 757679, upload-time = "2026-05-09T23:11:55.82Z" }, - { url = "https://files.pythonhosted.org/packages/04/2a/ff713fff0c566507c06a4ce2dc0ae8e7eeebc88811a95fc81cf1e7d534dd/regex-2026.5.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c010eb8caca74bdb40c07498d7ece26b4428fd3f04aa8a72c9ac6f79e8faaac6", size = 837116, upload-time = "2026-05-09T23:11:57.934Z" }, - { url = "https://files.pythonhosted.org/packages/77/90/df6d982b03e3614785c6937ba51b57f6733d97d2ee1c9bc7531dbfab3a54/regex-2026.5.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a6a563446a41adc451393dc6b8e6ad87979efaee3c8738690a8d1b08ebead1b4", size = 782081, upload-time = "2026-05-09T23:11:59.607Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/4e88a5f7c3e98489aac4dd23142723d907b2a595b4a6abcbacabefeded09/regex-2026.5.9-cp310-cp310-win32.whl", hash = "sha256:954cc214c04663ee6d266fc61739cad83054683048de65c5bd1d640ad28098ac", size = 266247, upload-time = "2026-05-09T23:12:01.116Z" }, - { url = "https://files.pythonhosted.org/packages/6a/40/4b224cb0582b2dca1786726e6cdabe26abbf757d7f6718332f186da155d2/regex-2026.5.9-cp310-cp310-win_amd64.whl", hash = "sha256:b310768746dd314ea6e2ff4cc89ef215426813396ff4e94ee8e6f7096c8b6e03", size = 278416, upload-time = "2026-05-09T23:12:03.2Z" }, - { url = "https://files.pythonhosted.org/packages/12/4d/014fbe803204cab0947ee428f09f658a29632053dde1d3c6176bb4f0fd4c/regex-2026.5.9-cp310-cp310-win_arm64.whl", hash = "sha256:19c16ceb4a267a8789e25733e583983eeab9f0f8664e66b0bd1c5d21f14c2d4b", size = 270413, upload-time = "2026-05-09T23:12:04.649Z" }, - { url = "https://files.pythonhosted.org/packages/c2/dc/c1f2df4027e82fc54b5a473e4b250f5139faca49a0fbe29a48668d228f34/regex-2026.5.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ccf5249114cc3e772ecdd88a98a86eca0fd74c61ce32a94743758c083fc05d48", size = 489445, upload-time = "2026-05-09T23:12:06.111Z" }, - { url = "https://files.pythonhosted.org/packages/03/d2/59f01110660081cce9c0bc30ebd0b5ee250dacf658e3248ed92f01e0e8ee/regex-2026.5.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46f1326ca6e65b0879d23ca302c0f2415aad42ff0309b9c818e7949fe19a41d8", size = 291271, upload-time = "2026-05-09T23:12:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/58/b6/14b2c84ff90ddb370c81d27503f4a0fcf071496416f4855f6cc8c5d81c35/regex-2026.5.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef31cbfe458e21c6122ba8150ff060e0c7789ed0d26eb423f25472584920b555", size = 289212, upload-time = "2026-05-09T23:12:09.266Z" }, - { url = "https://files.pythonhosted.org/packages/03/d0/4db86529117320de0c84afd90e70bb47434625875e34fcef9d8c127c5b16/regex-2026.5.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:992604d02e6d9c6d786c24a706a71ecffe1020fc1ef264044474cd81fa2c3919", size = 792310, upload-time = "2026-05-09T23:12:11.416Z" }, - { url = "https://files.pythonhosted.org/packages/07/78/fe4800cd322f862ecffd2d553409b20d80650e5ed71b9d178f853d020b82/regex-2026.5.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9411dd64ca95477225734a93dfc8583b51916b8d5942f99d6cac21e09965451", size = 861721, upload-time = "2026-05-09T23:12:13.681Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d0/b3618a895dd8feb897c61bb2954edd265e1767d82a01d53065d5871127a3/regex-2026.5.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4a3ff360dfb836fecdb93a4598f9d6e2ac81e3e397125145c6221bf58cf4c", size = 906460, upload-time = "2026-05-09T23:12:15.443Z" }, - { url = "https://files.pythonhosted.org/packages/33/6f/1481597e859ef19508b345eec4afd1416ed6e6b459c75a64026ef193aecf/regex-2026.5.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a661a7d270a61f7cf460caee8b9fa2d5ef9e5c681234bcb9e0fe14f488e7dfc", size = 799843, upload-time = "2026-05-09T23:12:16.892Z" }, - { url = "https://files.pythonhosted.org/packages/73/59/955734c803f59108deccba3597ae440c76b62a652733c0006e6243758420/regex-2026.5.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f079e50a0d3cc3cd5091fa9ff45869a2e6b2cd35895731edafb0327901a8d86d", size = 773610, upload-time = "2026-05-09T23:12:19.127Z" }, - { url = "https://files.pythonhosted.org/packages/68/8f/70c04a236d651c81881dac42ef8538bddda6121434509d0a22d9e601503b/regex-2026.5.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ebe8f0b5ec5a5024dc4a4c59f444c4e9afc5f2abdbb8962065b75d27fb971f9", size = 781645, upload-time = "2026-05-09T23:12:20.806Z" }, - { url = "https://files.pythonhosted.org/packages/1d/96/05c7434d88185e5d27fe54aeb74df86bd77cd79f52f0b4eae54faa8fea70/regex-2026.5.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:97cf3bc1b7d7d2306772ec07366c80d9df00ff79e79cea32898883a646d2fae2", size = 854473, upload-time = "2026-05-09T23:12:22.465Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c1/6e3d8202d981f3117004bf341ee74893ba4ba8a9fbaf4b94615846550a08/regex-2026.5.9-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0f9eede6a5cbdc02d4978090186390936e1776a7d1359b21e41014c609880bcf", size = 763311, upload-time = "2026-05-09T23:12:24.351Z" }, - { url = "https://files.pythonhosted.org/packages/93/c7/e7737f1526b3fb32bd4c337fd6c71c3ebb5c8296fc34d11197e0955d2e35/regex-2026.5.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:01f0f5f55f4b64dacec85dc116d3c05fd23ad3ff037bbc73a2085775953c2611", size = 844593, upload-time = "2026-05-09T23:12:26.341Z" }, - { url = "https://files.pythonhosted.org/packages/a5/27/0daffb1a535bb39f422c3d200f4ab023c71110ad66a32b366bee708baba0/regex-2026.5.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1268eddd8486dc561d08eee1156e40aa3a8fe10f4bdec8fa653b455fcbffd12c", size = 789167, upload-time = "2026-05-09T23:12:27.975Z" }, - { url = "https://files.pythonhosted.org/packages/ce/fc/294fe4fac4f2ed67207b17471815870c1c45b3a489e08e0ac96daea16ef6/regex-2026.5.9-cp311-cp311-win32.whl", hash = "sha256:8676474c07469d6f33dd1085ca2cd45f65785f32518f2b20e36d9953ca07f994", size = 266249, upload-time = "2026-05-09T23:12:30.141Z" }, - { url = "https://files.pythonhosted.org/packages/d0/b0/8dce459f6245bcf8f6e9f23ac9569f1a0f15c131cc0745e82b43226204cf/regex-2026.5.9-cp311-cp311-win_amd64.whl", hash = "sha256:246de9d60aa3f8538b519834dd95cbf276ea263d6a7bd5a3666dc3fa0230505b", size = 278423, upload-time = "2026-05-09T23:12:31.676Z" }, - { url = "https://files.pythonhosted.org/packages/db/8d/f9aeff6ad63a3ef720386f2907e6d34a35a510a6e498ebad28b0fb3f6ab6/regex-2026.5.9-cp311-cp311-win_arm64.whl", hash = "sha256:d726ca3f0d76969bf1e8e477d160d3d666bbf999f6860bd314889e5345782046", size = 270420, upload-time = "2026-05-09T23:12:33.194Z" }, - { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, - { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, - { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, - { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, - { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, - { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, - { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, - { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, - { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, - { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, - { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, - { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, - { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, - { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, - { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, - { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, - { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, - { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, - { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, - { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, - { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, - { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, - { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, - { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, - { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, - { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, - { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, - { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, - { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, - { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, - { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, - { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, - { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, - { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, - { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, - { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, - { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, - { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, - { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, - { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, - { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, - { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, - { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, - { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, - { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, - { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, - { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, - { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, - { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, - { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, - { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, - { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, - { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, - { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, - { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, - { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, - { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, - { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, - { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, -] - [[package]] name = "requests" version = "2.34.2" @@ -3829,32 +3370,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2e/a3/0f0b7d78e2f1eb9e8e1afbff1d2bff8d60144aee17aca51c065b516743dd/safehttpx-0.1.7-py3-none-any.whl", hash = "sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde", size = 8959, upload-time = "2025-10-24T18:30:08.733Z" }, ] -[[package]] -name = "safetensors" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, - { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, - { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, - { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, -] - [[package]] name = "secretstorage" version = "3.5.0" @@ -3877,15 +3392,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552, upload-time = "2022-05-26T13:35:21.206Z" }, ] -[[package]] -name = "setuptools" -version = "81.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, -] - [[package]] name = "shellingham" version = "1.5.4" @@ -3939,48 +3445,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, ] -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - -[[package]] -name = "tokenizers" -version = "0.22.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, - { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, - { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, - { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, -] - [[package]] name = "tomli" version = "2.4.1" @@ -4053,59 +3517,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, ] -[[package]] -name = "torch" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/b7/53fe0436586716ab7aecff41e26b9302d57c85ded481fd83a2cd741e6b4e/torch-2.12.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1834bd984f8a2f4f16bdfbeecca9146184b220aa46276bf5756735b5dae12812", size = 87981887, upload-time = "2026-05-13T14:55:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/34/60/d930eac44c30de06ed16f6d1ba4e785e1632532b50d8f0bf9bf699a4d0c7/torch-2.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d4d029801cb7b6df858804a2a21b00cc2aa0bf0ee5d2ab18d343c9e9e5681f35", size = 426355000, upload-time = "2026-05-13T14:54:31.944Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0c/c76b6a087820bab55705b94dfc074e520de9ae91f5ef90da2ecbf2a3ef12/torch-2.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d47e7dee68ac4cd7a068b26bcd6b989935427709fae1c8f7bd0019978f829e15", size = 532144998, upload-time = "2026-05-13T14:56:05.523Z" }, - { url = "https://files.pythonhosted.org/packages/4a/64/8a0d036e166a6aa85ee09bef072f3655d1ba5d5486a68d1b03b6813c01b3/torch-2.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:cf9839790285dd472e7a16aafcb4a4e6bf58ec1b494045044b0eefb0eb4bd1f2", size = 122949877, upload-time = "2026-05-13T14:55:46.841Z" }, - { url = "https://files.pythonhosted.org/packages/18/62/131124fb95df03811b8260d1d43dcc5ee85ea1a344b964613d7efe77fb08/torch-2.12.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:10802fd383bbfed646212e765a72c37d2185205d4f26eb197a254e8ac7ddcb25", size = 87990344, upload-time = "2026-05-13T14:55:42.154Z" }, - { url = "https://files.pythonhosted.org/packages/12/9c/dda0dbd547dc549839824135f223792fd0e725f28ed0715dda366b7acaa2/torch-2.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c12592630aef72feaf18bd3f197ef587bbfa21131b31c38b23ab2e55fce92e36", size = 426362932, upload-time = "2026-05-13T14:54:15.295Z" }, - { url = "https://files.pythonhosted.org/packages/e2/d2/a7dd5a3f9bdaa7842124e8e2359202b317c48d47d2fc5816fafdf2049adb/torch-2.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:415c1b8d0412f67551c8e89a2daca0fb3e56694af0281ba155eaa9da481f58b4", size = 532170085, upload-time = "2026-05-13T14:55:20.788Z" }, - { url = "https://files.pythonhosted.org/packages/12/1b/a61ce2004f9ab0ea8964a6e6168133a127795667639e2ff4f8f2bdb16a65/torch-2.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd37188ea325042cb1f6cafa56822b11ada2520c04791a52629b0af25bdfbfd9", size = 122953128, upload-time = "2026-05-13T14:54:52.744Z" }, - { url = "https://files.pythonhosted.org/packages/ef/bb/285d643f254731294c9b595a007eac39db4600a98682d7bca688f42ca164/torch-2.12.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b41339df93d491435e790ff8bcbae1c0ce777175889bfd1281d119862793e6a2", size = 88010197, upload-time = "2026-05-13T14:55:35.414Z" }, - { url = "https://files.pythonhosted.org/packages/79/81/76debf1db1343bd929bbb5d74c89fb437c2ed88eb144712557e7bd3eea45/torch-2.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8fbef9f108a863e7722a73740998967e3b074742a834fc5be3a535a2befa7057", size = 426376751, upload-time = "2026-05-13T14:55:03.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/f0/80026028b603c4650ff270fc3785bdef4bd6738765a9cc5a0f5a637d65a2/torch-2.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4b4f64c2c2b11f7510d93dd6412b87025ff6eddd6bb61c3b5a3d892ea20c4756", size = 532261691, upload-time = "2026-05-13T14:52:54.453Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c2/64b06cbb7830fb3cd9be13e1158b31a3f36b68e6a209105ee3c9d9480be0/torch-2.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b958caff4a14d3a3b0b2dfc6a378f64dda9728a9dad28c08a0db9ce4dafb549", size = 122988114, upload-time = "2026-05-13T14:54:42.153Z" }, - { url = "https://files.pythonhosted.org/packages/86/ca/01896c80ba921676aa45886b2c5b8d774912de2a1f719de48169c6f755cd/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", size = 88009511, upload-time = "2026-05-13T14:54:47.411Z" }, - { url = "https://files.pythonhosted.org/packages/a5/04/52bdaf4787eab6ac7d7f5851dff934e4def0bc8ead9c8fd2b69b3e529699/torch-2.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:864392c73b7654f4d2b3ae712f607937d0dbb1101c4555fbb41848106b297f39", size = 426383231, upload-time = "2026-05-13T14:53:32.129Z" }, - { url = "https://files.pythonhosted.org/packages/49/8a/94bdecd13f5aaa90d45920b89789d9fe7c6f4af8c3cdd7ce01fcb59908fc/torch-2.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5d6b560dfa7d56291c07d615c3bb73e8d9943d9b6d87f76cd0d9d570c4797fa6", size = 532269288, upload-time = "2026-05-13T14:53:49.423Z" }, - { url = "https://files.pythonhosted.org/packages/3e/2f/bdbaaa267de519ef1b73054bf590d8c93c37a266c9a4e24a01bd38b6918f/torch-2.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:3fee918902090ade827643e758e98363278815de583c75d111fdd665ebffde9f", size = 122987706, upload-time = "2026-05-13T14:54:00.335Z" }, - { url = "https://files.pythonhosted.org/packages/9b/ad/e95e822f3538171e22640a7fbe839a1fdb666600bf6487025de2ff03b11a/torch-2.12.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:10ee1448a9f304d3b987eb4656f664ba6e4d7b410ca7a5a7c642199777a2cf88", size = 88319556, upload-time = "2026-05-13T14:54:05.574Z" }, - { url = "https://files.pythonhosted.org/packages/b7/07/055d06d985b445d67422d25b033c11cf55bbb81785d4c4e68e28bca5820e/torch-2.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:af68dbf403439cae9ceaeaaf92f8352b460787dcd27b92aa05c40dd4a19c0f1e", size = 426397656, upload-time = "2026-05-13T14:52:38.84Z" }, - { url = "https://files.pythonhosted.org/packages/43/94/b0b4fdc3014122e0a7302fb90086d352aa48f2576f0b252561ebb38c01a8/torch-2.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a6a2eebb237d3b1d9ad3b378e86d9b9e0782afdea8b1e0eba6a13646b9b49c07", size = 532183124, upload-time = "2026-05-13T14:53:16.178Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c8/052405e6ad05d3237bfe5a4df78f917773956f8e17813a2d44c059068b74/torch-2.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2140e373e9a51a3e22ef62e8d14366d0b470d18f0adf19fdc757368077133a34", size = 123232462, upload-time = "2026-05-13T14:52:27.26Z" }, - { url = "https://files.pythonhosted.org/packages/67/dc/ac069f8d6e8be701535921141055293b0d4819d3d7f224a4612cf157c7f9/torch-2.12.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7dfae4a519197dfa050e98d8e36378a0fb5899625a875c2b54445005a2e404e", size = 88027282, upload-time = "2026-05-13T14:53:05.258Z" }, - { url = "https://files.pythonhosted.org/packages/33/c3/1c1eb00e34555b536dddf792676026a988d710ed36981aa00499b36b0620/torch-2.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:891c769072637c74e9a5a77a3bc782894696d8ffec83b938df8536dee7f0ba78", size = 426386961, upload-time = "2026-05-13T14:51:28.406Z" }, - { url = "https://files.pythonhosted.org/packages/cd/d4/7e730dba0c7032a4154dc9056b76cf9625515e030e269cfbf8098fcfee7d/torch-2.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e2ad3eb85d39c3cab62dfa93ed5a73516e6a53c6713cb97d004004fe089f0f1f", size = 532272265, upload-time = "2026-05-13T14:51:59.308Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b4/92c80d1bbfee1c0036c06d1d2155a3065bd2423134c83bf8a47e65cd6b9b/torch-2.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:c66696857e987efb8bc1777a37357ec4f60ab5e8af6250b83d6034437fa2d8f3", size = 122987138, upload-time = "2026-05-13T14:51:45.942Z" }, - { url = "https://files.pythonhosted.org/packages/7b/78/2e12b37ce50a19a037d7bc62d652a5a8f27385a7b05859d6bc9204f20cfe/torch-2.12.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b4556715c8572758625d62b6e0ae3b1f76c440221913a6fb5e100f321fb4fb02", size = 88320100, upload-time = "2026-05-13T14:51:39.955Z" }, - { url = "https://files.pythonhosted.org/packages/56/5e/83c450ec7b0bb40a7b74611c1b5440f9260e33c54c90d556fd4a1f0fd955/torch-2.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a43ac605a5e13116c72b64c359644cce0229f213dde48d2ae0ae5eb5becf7feb", size = 426391871, upload-time = "2026-05-13T14:52:14.989Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e9/1a0b575d98d0afedd8f157d23fa3d2759421483660448e60d0a4b10b6daa/torch-2.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a7512adfdd7f6732e40de1c620831e3c75b39b98cef60b11d0c5f0a76473ec5", size = 532192241, upload-time = "2026-05-13T14:51:07.795Z" }, - { url = "https://files.pythonhosted.org/packages/88/21/afadd25ecd81b3cea1e11c73cf1ab41a983a50271548c3ec7ec3b9efc3e9/torch-2.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f96b63f8287f66a005dd1b5a6abba2920f11156c5e5c4d815f3e2050fd1aa16", size = 123231092, upload-time = "2026-05-13T14:51:18.854Z" }, -] - [[package]] name = "tqdm" version = "4.67.3" @@ -4118,84 +3529,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] -[[package]] -name = "trackio" -version = "0.25.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gradio-client" }, - { name = "huggingface-hub" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "orjson" }, - { name = "pillow" }, - { name = "python-multipart" }, - { name = "starlette" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "uvicorn", extra = ["standard"] }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9a/80caa250ef9e0e465db883958764c9b27a413c8b246349f60c237b820201/trackio-0.25.1-py3-none-any.whl", hash = "sha256:d019b79a8dfb14264db1055b7e9b2681984138b827dabd5ec01585c0bd24116d", size = 1653922, upload-time = "2026-04-26T02:13:41.814Z" }, -] - -[[package]] -name = "transformers" -version = "5.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e7/e6/4134ea2fbea322cddc7ffc94a0d8ee47fe32ce8e876b320cd37d88edfc4d/transformers-5.8.1.tar.gz", hash = "sha256:4dd5b6de4105725104d84fd6abd74b305f4debfc251b38c648ee5dd087cf543b", size = 8532019, upload-time = "2026-05-13T03:21:57.234Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/b1/8be7e7ef0b5200491312201918b6125ef9c9df9dd0f0240ccef9ac824e6b/transformers-5.8.1-py3-none-any.whl", hash = "sha256:5340fb95962162cdfdae5cc91d7f8fedd92ed75216c1154c5e1f590fcf56dd0e", size = 10632882, upload-time = "2026-05-13T03:21:52.876Z" }, -] - -[[package]] -name = "triton" -version = "3.7.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/97/dcd1f2a0f8336691bff74abc59b2ed9c69a0c0f8f65cd77109c49e05f068/triton-3.7.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223ac302091491436c248a34ee1e6c47a1026486579103c906ffd805be50cb89", size = 188367104, upload-time = "2026-05-07T19:04:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c0/c2ac4fd2d8809b7579d4a820a0f9e5de62a9bc8a757ed4b3abf4f7ee964a/triton-3.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c631b65668d4951213b948a413c0564184305b77bb45cc9d686d3e1ecc4701a3", size = 201313191, upload-time = "2026-05-07T18:45:58.444Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c1/5d842314bb6c78442cc60437928781701c6050b8d479bc2a1aed691d37ca/triton-3.7.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9e71fc392675fac364e0ecf4ef3f76f85b7f5433a16f4c3c5fe5f05a52c85fe", size = 188480277, upload-time = "2026-05-07T19:05:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/13/31/8315ea5f8dd18e60970b3022e3a8b93fd37e0b784fbbef86e10c8e6e5ca1/triton-3.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22bacffce443f54593dd20f05294d5a40622e0ea9ab632816f87154504356221", size = 201415942, upload-time = "2026-05-07T18:46:06.479Z" }, - { url = "https://files.pythonhosted.org/packages/f7/13/ec05adfcd87311d532ba61e3af143e8be59fcd26675884c4682841406a20/triton-3.7.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4bf49b00a7a377a68a6da603a876e797614e6455a80e9021669c476a953ad9a", size = 188505104, upload-time = "2026-05-07T19:05:09.843Z" }, - { url = "https://files.pythonhosted.org/packages/62/7b/468a576e35beef1426e0828e28e9ba9e65f5474d496f16ee126c15646324/triton-3.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f111161d49bf903c0eaedde3962353a3d841c08a836839b7cc1025b8426efcf", size = 201457567, upload-time = "2026-05-07T18:46:13.505Z" }, - { url = "https://files.pythonhosted.org/packages/01/e1/a59a583de59b8f62c495d67c80ee3ea97d09e91ac80c4c6e76456ed8d8ac/triton-3.7.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abdf6beaa89b1bcfb9a43cd990536ce66091a997841a4814b260b7bee4c88c3c", size = 188503209, upload-time = "2026-05-07T19:05:17.935Z" }, - { url = "https://files.pythonhosted.org/packages/30/b1/b7507bb9815d403927c8dd51d4158ed2e11751a92dbc118a044f247b6848/triton-3.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a35d7afe3f3f058e7ec49fcce09794049e0ffc5c59019ac25ec3413741b8c4e7", size = 201453566, upload-time = "2026-05-07T18:46:20.427Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8f/0bea7a6a0c989315c9135a1d7fb37e41905cfb3a17cbc1f10044ebd4cc3a/triton-3.7.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc1d61c172d257db80ddf42595131fb196ad2e9bdd751e90fe2ef13531734e8b", size = 188612899, upload-time = "2026-05-07T19:05:24.955Z" }, - { url = "https://files.pythonhosted.org/packages/e1/02/d96f57828d0912aec733b9bc7e0e7dbfd2c6f079a8fa433ac25cb93d1a30/triton-3.7.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70fb9bbdc9f400afc54bbf6eb2670af28829a6ae3996863317964783141daf56", size = 201553816, upload-time = "2026-05-07T18:46:27.49Z" }, - { url = "https://files.pythonhosted.org/packages/40/fb/82a802dac4689f2a2fb2e69302e6a138eecc3e175bbe976ba3cfc717683a/triton-3.7.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a44a8476d0d3571eac4e4d1048e1ff75aad81a09ff4602ccfc56c6dea1672e", size = 188507879, upload-time = "2026-05-07T19:05:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/8f/af/9904ec6d3c93d9b24e5ec360445bbdf758b7f00bfbeedb89cb0eb64eb8bb/triton-3.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9b85e72968a9d8bba5ddb24e9b64aaabaf48affb042f2755cb7cfa92b7531ce", size = 201460637, upload-time = "2026-05-07T18:46:34.749Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f9/4835a8ea746b88727d8899f4e3ccce4f9cacb38abfc3bb0a638266c53111/triton-3.7.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18a160de426fd99f92b0baf509045360afbd3bfaa0b4a5171dde800ec9f09684", size = 188608706, upload-time = "2026-05-07T19:05:39.218Z" }, - { url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766, upload-time = "2026-05-07T18:46:42.088Z" }, -] - -[[package]] -name = "trl" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "accelerate" }, - { name = "datasets" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "transformers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/fe/eaa56e5d1bb7c418340dcc97c7d1a724dda0d42419e1b3c16c230af942b1/trl-1.4.0.tar.gz", hash = "sha256:37f9b9a4401922469f02be85c6230ddfc680d746338c5d3f6fc5641fbff83908", size = 619070, upload-time = "2026-05-08T23:24:14.346Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/f4/74bf11119cad07b3cd72b0c7213c5e90b17025b4af429418b72314b245bc/trl-1.4.0-py3-none-any.whl", hash = "sha256:d8c1aeaa196cdaf2156206aa087d8c332c59e197d70b07585a1228abc19822c6", size = 751040, upload-time = "2026-05-08T23:24:12.989Z" }, -] - [[package]] name = "typer" version = "0.25.1" diff --git a/examples/mini_swe_env/README.md b/examples/mini_swe_env/README.md index f5958e98e..141353791 100644 --- a/examples/mini_swe_env/README.md +++ b/examples/mini_swe_env/README.md @@ -46,7 +46,7 @@ The agent **cannot** influence the training reward. The in-sandbox |------|---------| | `run_swe_sample.py` | Real end-to-end interception rollout with a live LLM | | `train_swe_async_grpo.py` | AsyncGRPO trainer entrypoint wired to SWE custom rollout worker | -| `envs/mini_swe_env/async_grpo/` | Control plane, worker, and trainer wiring modules | +| `examples/mini_swe_env/async_grpo/` | Control plane, worker, and trainer wiring modules | ## Quick Start diff --git a/envs/mini_swe_env/async_grpo/__init__.py b/examples/mini_swe_env/async_grpo/__init__.py similarity index 100% rename from envs/mini_swe_env/async_grpo/__init__.py rename to examples/mini_swe_env/async_grpo/__init__.py diff --git a/envs/mini_swe_env/async_grpo/control_plane.py b/examples/mini_swe_env/async_grpo/control_plane.py similarity index 100% rename from envs/mini_swe_env/async_grpo/control_plane.py rename to examples/mini_swe_env/async_grpo/control_plane.py diff --git a/envs/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py similarity index 99% rename from envs/mini_swe_env/async_grpo/rollout_worker.py rename to examples/mini_swe_env/async_grpo/rollout_worker.py index 1a0663d05..3de22cb05 100644 --- a/envs/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -54,7 +54,7 @@ get_ip = None get_open_port = None -from ..models import SWETask +from mini_swe_env.models import SWETask _log = logging.getLogger(__name__) diff --git a/envs/mini_swe_env/async_grpo/space_app/Dockerfile b/examples/mini_swe_env/async_grpo/space_app/Dockerfile similarity index 100% rename from envs/mini_swe_env/async_grpo/space_app/Dockerfile rename to examples/mini_swe_env/async_grpo/space_app/Dockerfile diff --git a/envs/mini_swe_env/async_grpo/space_app/README.md b/examples/mini_swe_env/async_grpo/space_app/README.md similarity index 100% rename from envs/mini_swe_env/async_grpo/space_app/README.md rename to examples/mini_swe_env/async_grpo/space_app/README.md diff --git a/envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh similarity index 97% rename from envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh rename to examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index 8430a1deb..1867b6c72 100755 --- a/envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -15,7 +15,7 @@ # - Python with huggingface_hub installed # # Usage: -# bash envs/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh [OPTIONS] +# bash examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh [OPTIONS] # # Options: # --space-id OWNER/NAME Space ID (default: $HF_SPACE_ID or rycerzes/swe-async-grpo-train) @@ -213,15 +213,16 @@ else envs/mini_swe_env/ "$STAGE_DIR/envs/mini_swe_env/" mkdir -p "$STAGE_DIR/examples/mini_swe_env" - cp examples/mini_swe_env/train_swe_async_grpo.py "$STAGE_DIR/examples/mini_swe_env/" + rsync -a --exclude='__pycache__' --exclude='*.pyc' \ + examples/mini_swe_env/ "$STAGE_DIR/examples/mini_swe_env/" cp pyproject.toml "$STAGE_DIR/" cp LICENSE "$STAGE_DIR/" cp .gitignore "$STAGE_DIR/" # Space-specific files at root - cp envs/mini_swe_env/async_grpo/space_app/Dockerfile "$STAGE_DIR/Dockerfile" - cp envs/mini_swe_env/async_grpo/space_app/start.sh "$STAGE_DIR/start.sh" + cp examples/mini_swe_env/async_grpo/space_app/Dockerfile "$STAGE_DIR/Dockerfile" + cp examples/mini_swe_env/async_grpo/space_app/start.sh "$STAGE_DIR/start.sh" # Space README (metadata) cat > "$STAGE_DIR/README.md" << EOF diff --git a/envs/mini_swe_env/async_grpo/space_app/start.sh b/examples/mini_swe_env/async_grpo/space_app/start.sh similarity index 100% rename from envs/mini_swe_env/async_grpo/space_app/start.sh rename to examples/mini_swe_env/async_grpo/space_app/start.sh diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 277153fcf..6b6a204a3 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -36,7 +36,7 @@ from typing import Any _root = Path(__file__).resolve().parent.parent.parent -for _p in (_root / "src", _root / "envs"): +for _p in (_root, _root / "src", _root / "envs"): if str(_p) not in sys.path: sys.path.insert(0, str(_p)) @@ -44,11 +44,11 @@ from transformers import AutoTokenizer # noqa: E402 from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer # noqa: E402 -from mini_swe_env.async_grpo.control_plane import ( # noqa: E402 +from examples.mini_swe_env.async_grpo.control_plane import ( # noqa: E402 SWEAsyncControlPlane, SWEAsyncControlPlaneConfig, ) -from mini_swe_env.async_grpo.rollout_worker import ( # noqa: E402 +from examples.mini_swe_env.async_grpo.rollout_worker import ( # noqa: E402 SWERolloutWorker, WorkerConfig, ) @@ -82,8 +82,9 @@ def start_interception_server( # Start the server on the new loop. future = asyncio.run_coroutine_threadsafe(control_plane.start(), loop) # The loop must be running for the coroutine to execute. - thread = threading.Thread(target=_run_event_loop, args=(loop,), daemon=True, - name="interception-server") + thread = threading.Thread( + target=_run_event_loop, args=(loop,), daemon=True, name="interception-server" + ) thread.start() # Wait for start() to complete. future.result(timeout=30) @@ -195,7 +196,9 @@ def _build_checkpoint_args() -> tuple[dict[str, Any], str | None, bool]: elif resume_pref == "auto": resume_from_checkpoint = "last-checkpoint" else: - resume_from_checkpoint = os.environ.get("SWE_RESUME_FROM_CHECKPOINT", "").strip() + resume_from_checkpoint = os.environ.get( + "SWE_RESUME_FROM_CHECKPOINT", "" + ).strip() return checkpoint_args, resume_from_checkpoint, True @@ -237,13 +240,15 @@ def main() -> int: _log.info("loaded %d tasks", len(swe_tasks)) # ── Dataset (prompt per task) ───────────────────────────────── - dataset = Dataset.from_list([ - { - "prompt": [{"role": "user", "content": t.instruction}], - "instance_id": t.instance_id, - } - for t in swe_tasks - ]) + dataset = Dataset.from_list( + [ + { + "prompt": [{"role": "user", "content": t.instruction}], + "instance_id": t.instance_id, + } + for t in swe_tasks + ] + ) # ── Tokenizer ───────────────────────────────────────────────── tokenizer = AutoTokenizer.from_pretrained(model) @@ -293,7 +298,9 @@ def _noop_reward(**kwargs: Any) -> list[float]: prompts = kwargs.get("prompts", []) return [0.0] * len(prompts) - checkpoint_args, resume_from_checkpoint, checkpoint_requested = _build_checkpoint_args() + checkpoint_args, resume_from_checkpoint, checkpoint_requested = ( + _build_checkpoint_args() + ) async_grpo_args: dict[str, Any] = { "output_dir": os.path.join( os.environ.get("HOME", "/tmp"), "outputs/swe_async_grpo" @@ -351,7 +358,10 @@ def _noop_reward(**kwargs: Any) -> list[float]: try: trainer.train(resume_from_checkpoint=resume_from_checkpoint) except Exception as exc: - if resume_from_checkpoint == "last-checkpoint" and _is_missing_checkpoint_error(exc): + if ( + resume_from_checkpoint == "last-checkpoint" + and _is_missing_checkpoint_error(exc) + ): _log.warning( "No hub checkpoint found at 'last-checkpoint'; starting from scratch" ) diff --git a/tests/envs/test_swe_async_rollout_worker.py b/tests/envs/test_swe_async_rollout_worker.py index 772bf9629..a8bb0e04c 100644 --- a/tests/envs/test_swe_async_rollout_worker.py +++ b/tests/envs/test_swe_async_rollout_worker.py @@ -1,4 +1,11 @@ -from mini_swe_env.async_grpo.rollout_worker import ( +import sys +from pathlib import Path + +_ROOT = Path(__file__).resolve().parents[2] +if str(_ROOT) not in sys.path: + sys.path.insert(0, str(_ROOT)) + +from examples.mini_swe_env.async_grpo.rollout_worker import ( _get_tools, _has_answer_call, _is_terminal_non_tool_response, From 42419d65ad6c9a27c6b7f41b2761e49538b17081 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 23 May 2026 18:57:33 +0530 Subject: [PATCH 57/79] fix(pi): use Node.js 22.x --- src/openenv/core/harness/agents/pi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openenv/core/harness/agents/pi.py b/src/openenv/core/harness/agents/pi.py index 060b41dbd..a2fdd7537 100644 --- a/src/openenv/core/harness/agents/pi.py +++ b/src/openenv/core/harness/agents/pi.py @@ -166,7 +166,7 @@ def _build_env_vars(spec: CLIAgentSpec, config: Any) -> dict[str, str]: setup=( "set -e && " "apt-get update -qq && apt-get install -y -qq curl ca-certificates gnupg && " - "curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && " + "curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && " "apt-get install -y -qq nodejs && " "curl -fsSL https://pi.dev/install.sh | sh && " "mkdir -p /home/user/logs/agent /home/user/task /home/user/workdir && " From 9e61e7666a8d0f5967e5b5e595cfb57e5f0c3e7f Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 23 May 2026 19:02:46 +0530 Subject: [PATCH 58/79] fix: env handling in deployment scripts --- .../async_grpo/space_app/deploy_hf_space.sh | 115 +++++++++++++----- .../async_grpo/space_app/start.sh | 10 +- 2 files changed, 96 insertions(+), 29 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index 1867b6c72..f4d4a4862 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -12,7 +12,7 @@ # Prerequisites: # - hf CLI installed and authenticated (hf auth login) # - HF_TOKEN set or in ~/.cache/huggingface/token -# - Python with huggingface_hub installed +# - Python with huggingface_hub installed in repo .venv (preferred), or uv/python3 available # # Usage: # bash examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh [OPTIONS] @@ -48,6 +48,34 @@ MONITOR=false PAUSE=false RESUME=false +# ── Tooling prerequisites ─────────────────────────────────────────────── +# Prefer repo-local venv Python, then uv, then system python3. +if [ -x "$REPO_ROOT/.venv/bin/python" ]; then + PYTHON_CMD=("$REPO_ROOT/.venv/bin/python") +elif [ -x "$REPO_ROOT/.venv/Scripts/python.exe" ]; then + PYTHON_CMD=("$REPO_ROOT/.venv/Scripts/python.exe") +elif command -v uv >/dev/null 2>&1; then + PYTHON_CMD=(uv run python) +elif command -v python3 >/dev/null 2>&1; then + PYTHON_CMD=("$(command -v python3)") +else + echo "ERROR: Python interpreter not found." + echo "Expected one of:" + echo " - $REPO_ROOT/.venv/bin/python" + echo " - $REPO_ROOT/.venv/Scripts/python.exe" + echo " - uv run python" + echo " - python3" + exit 1 +fi + +if ! command -v hf >/dev/null 2>&1; then + echo "ERROR: hf CLI not found. Install huggingface_hub CLI and run 'hf auth login'." + exit 1 +fi + +# Ensure Python subprocesses use UTF-8 output (avoids cp1252 emoji crashes on Windows). +export PYTHONUTF8=1 + # ── Parse args ────────────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do case "$1" in @@ -81,7 +109,7 @@ export HF_TOKEN # ── Pause/Resume shortcuts ────────────────────────────────────────────── if [ "$PAUSE" = true ]; then echo "⏸ Pausing Space $SPACE_ID..." - python3 -c " + "${PYTHON_CMD[@]}" -c " from huggingface_hub import HfApi api = HfApi(token='$HF_TOKEN') api.pause_space('$SPACE_ID') @@ -92,7 +120,7 @@ fi if [ "$RESUME" = true ]; then echo "▶ Resuming Space $SPACE_ID..." - python3 -c " + "${PYTHON_CMD[@]}" -c " from huggingface_hub import HfApi api = HfApi(token='$HF_TOKEN') api.restart_space('$SPACE_ID', factory_reboot=True) @@ -117,12 +145,12 @@ echo "" # ── Step 1: Create Space (idempotent) ────────────────────────────────── echo "[1/6] Creating Space (if needed)..." -hf repos create "$SPACE_ID" --type space --space-sdk docker --public --exist-ok 2>/dev/null || true +hf repo create "$SPACE_ID" --repo-type space --space-sdk docker --no-private --exist-ok >/dev/null echo " ✓ Space exists: https://huggingface.co/spaces/$SPACE_ID" # ── Step 2: Configure secrets ────────────────────────────────────────── echo "[2/6] Configuring secrets and variables..." -python3 << PYEOF +"${PYTHON_CMD[@]}" << PYEOF from huggingface_hub import HfApi import os, secrets @@ -178,7 +206,7 @@ PYEOF # ── Step 3: Set hardware ─────────────────────────────────────────────── echo "[3/6] Setting hardware to $HARDWARE..." -python3 -c " +"${PYTHON_CMD[@]}" -c " from huggingface_hub import HfApi import os api = HfApi(token=os.environ['HF_TOKEN']) @@ -197,24 +225,55 @@ else cd "$REPO_ROOT" - # Copy essential files only - rsync -a --exclude='.git' --exclude='.venv' --exclude='__pycache__' \ - --exclude='.worktrees' --exclude='node_modules' --exclude='*.pyc' \ - --exclude='.tox' --exclude='.mypy_cache' --exclude='.pytest_cache' \ - --exclude='*.egg-info' --exclude='uv.lock' --exclude='.ruff_cache' \ - --exclude='docs' --exclude='tests' --exclude='.claude' --exclude='.agents' \ - --exclude='.codex' --exclude='.github' --exclude='rfcs' --exclude='tutorial' \ - --exclude='scripts' --exclude='.env' \ - src/ "$STAGE_DIR/src/" - - mkdir -p "$STAGE_DIR/envs" - rsync -a --exclude='__pycache__' --exclude='*.pyc' --exclude='uv.lock' \ - --exclude='.venv' \ - envs/mini_swe_env/ "$STAGE_DIR/envs/mini_swe_env/" - - mkdir -p "$STAGE_DIR/examples/mini_swe_env" - rsync -a --exclude='__pycache__' --exclude='*.pyc' \ - examples/mini_swe_env/ "$STAGE_DIR/examples/mini_swe_env/" + # Copy essential files only (Python-based to avoid rsync shell/path quirks on Windows). + export REPO_ROOT STAGE_DIR + "${PYTHON_CMD[@]}" << 'PYEOF' +import fnmatch +import os +import shutil +from pathlib import Path + +repo = Path(os.environ["REPO_ROOT"]) +stage = Path(os.environ["STAGE_DIR"]) + + +def _ignore(patterns: list[str]): + def _inner(_dir: str, names: list[str]) -> set[str]: + ignored: set[str] = set() + for name in names: + for pat in patterns: + if fnmatch.fnmatch(name, pat): + ignored.add(name) + break + return ignored + + return _inner + + +def copy_tree(src_rel: str, dst_rel: str, patterns: list[str]) -> None: + src = repo / src_rel + dst = stage / dst_rel + if not src.exists(): + raise FileNotFoundError(f"Missing source path: {src}") + shutil.copytree(src, dst, dirs_exist_ok=True, ignore=_ignore(patterns)) + + +common_ignores = [ + "__pycache__", + "*.pyc", + ".venv", + ".tox", + ".mypy_cache", + ".pytest_cache", + ".ruff_cache", + "*.egg-info", + "uv.lock", +] + +copy_tree("src", "src", common_ignores) +copy_tree("envs/mini_swe_env", "envs/mini_swe_env", common_ignores) +copy_tree("examples/mini_swe_env", "examples/mini_swe_env", ["__pycache__", "*.pyc"]) +PYEOF cp pyproject.toml "$STAGE_DIR/" cp LICENSE "$STAGE_DIR/" @@ -250,8 +309,8 @@ EOF # Auto-generated training args from deploy script # --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS EOF - # Actually replace the exec line to include our args - sed -i "s|exec python3 examples/mini_swe_env/train_swe_async_grpo.py|exec python3 examples/mini_swe_env/train_swe_async_grpo.py --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS|" "$STAGE_DIR/start.sh" + # Append deploy-time args to the trainer exec line (interpreter-agnostic). + sed -i "s|\(exec .*examples/mini_swe_env/train_swe_async_grpo.py\)|\1 --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS|" "$STAGE_DIR/start.sh" FILE_COUNT=$(find "$STAGE_DIR" -type f | wc -l) echo " ✓ Staged $FILE_COUNT files" @@ -259,7 +318,7 @@ EOF # ── Step 5: Upload to Space ──────────────────────────────────────── echo "[5/6] Uploading to Space..." cd "$STAGE_DIR" - hf upload "$SPACE_ID" . --type space \ + hf upload "$SPACE_ID" . --repo-type space \ --commit-message "Deploy: $MODEL, $MAX_TASKS tasks, $MAX_STEPS steps" 2>&1 | tail -3 echo " ✓ Upload complete" fi @@ -273,7 +332,7 @@ echo "" if [ "$MONITOR" = true ]; then echo "Monitoring build (Ctrl+C to stop)..." - python3 << PYEOF + "${PYTHON_CMD[@]}" << PYEOF from huggingface_hub import HfApi import time, os diff --git a/examples/mini_swe_env/async_grpo/space_app/start.sh b/examples/mini_swe_env/async_grpo/space_app/start.sh index 7d69f7215..7a4587a3d 100755 --- a/examples/mini_swe_env/async_grpo/space_app/start.sh +++ b/examples/mini_swe_env/async_grpo/space_app/start.sh @@ -17,6 +17,14 @@ VLLM_KEY="${VLLM_API_KEY:-token}" MAX_MODEL_LEN="${MAX_MODEL_LEN:-4096}" GPU_MEM_UTIL="${GPU_MEMORY_UTILIZATION:-0.9}" +# Prefer repo-local venv Python for local/dev runs; fall back to image python. +PYTHON_BIN="${PYTHON_BIN:-python3}" +if [ -x "$HOME/app/.venv/bin/python" ]; then + PYTHON_BIN="$HOME/app/.venv/bin/python" +elif [ -x "$HOME/app/.venv/Scripts/python.exe" ]; then + PYTHON_BIN="$HOME/app/.venv/Scripts/python.exe" +fi + # GPU assignment. On 2-GPU Spaces: vLLM=0, trainer=1. # On single GPU: both empty (share GPU 0). VLLM_GPU="${VLLM_GPU:-0}" @@ -77,6 +85,6 @@ fi # ── 3. Start trainer (foreground) ────────────────────────────── echo "[start.sh] Starting trainer on GPU $TRAINER_GPU..." -CUDA_VISIBLE_DEVICES="$TRAINER_GPU" exec python3 examples/mini_swe_env/train_swe_async_grpo.py \ +CUDA_VISIBLE_DEVICES="$TRAINER_GPU" exec "$PYTHON_BIN" examples/mini_swe_env/train_swe_async_grpo.py \ --vllm-url "http://127.0.0.1:${VLLM_PORT}" \ "$@" From c3228f710c1cf24a97c2b1bae661d3bb72625675 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 24 May 2026 01:29:42 +0530 Subject: [PATCH 59/79] chore: update vLLM version to 0.21.0 and adjust weight lifecycle management --- .../mini_swe_env/async_grpo/rollout_worker.py | 52 ++- .../async_grpo/space_app/Dockerfile | 20 +- .../async_grpo/space_app/deploy_hf_space.sh | 2 +- .../async_grpo/space_app/start.sh | 15 +- examples/mini_swe_env/test_version_compat.py | 296 ++++++++++++++++++ 5 files changed, 375 insertions(+), 10 deletions(-) create mode 100644 examples/mini_swe_env/test_version_compat.py diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index 3de22cb05..f46cd4df8 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -59,6 +59,27 @@ _log = logging.getLogger(__name__) +def _vllm_version() -> tuple[int, ...]: + """Return parsed vLLM version as a tuple, e.g. (0, 20, 2). + + Returns (0, 0, 0) if vLLM is not installed or version cannot be parsed. + """ + try: + import vllm # noqa: F811 + + parts = vllm.__version__.split(".") + return tuple(int(p) for p in parts[:3]) + except Exception: + return (0, 0, 0) + + +# vLLM 0.21+ uses a four-phase weight transfer protocol: +# init_weight_transfer_engine → start_weight_update → update_weights → finish_weight_update +# start_weight_update(is_checkpoint_format=True) routes through model.load_weights() +# which handles parameter name remapping (e.g. CausalLM → VLM prefixes). +_VLLM_NEEDS_WEIGHT_UPDATE_LIFECYCLE = _vllm_version() >= (0, 21, 0) + + # ── Sample dataclass ─────────────────────────────────────────────────── @@ -142,6 +163,13 @@ def __init__( self._started = False self._model_update_group: Any | None = None + # Prefix to prepend to trainer parameter names so they match vLLM's + # model architecture. For VLM models like Qwen3_5ForConditionalGeneration + # served with --language-model-only, vLLM parameters are at + # "language_model.model.layers.X" but the trainer (AutoModelForCausalLM) + # produces "model.layers.X". Set to "" to disable remapping. + self._vllm_weight_prefix = "language_model." + self._init_weight_transfer() # ── RolloutWorkerProtocol ────────────────────────────────────── @@ -201,6 +229,14 @@ def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: return names = [name for name, _ in items] + + # VLM models (e.g. Qwen3_5ForConditionalGeneration) have parameters at + # language_model.model.layers.X but the trainer (AutoModelForCausalLM) + # produces names like model.layers.X. Prepend the prefix so names match. + if names and not names[0].startswith("language_model."): + if self._vllm_weight_prefix: + names = [f"{self._vllm_weight_prefix}{n}" for n in names] + dtype_names = [ str(getattr(tensor, "dtype", "float32")).split(".")[-1] for _, tensor in items @@ -211,10 +247,20 @@ def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: "dtype_names": dtype_names, "shapes": shapes, "packed": True, - "is_checkpoint_format": True, } with self._weight_sync_lock: + # vLLM 0.21+ four-phase protocol: + # start_weight_update(is_checkpoint_format=True) → update_weights → finish_weight_update + # is_checkpoint_format=True tells vLLM to use model.load_weights() which + # handles parameter name remapping (e.g. CausalLM → VLM prefixes). + if _VLLM_NEEDS_WEIGHT_UPDATE_LIFECYCLE: + self._post_json( + "/start_weight_update", + timeout=60, + json_body={"is_checkpoint_format": True}, + ) + post_error: list[Exception] = [] def _post_update() -> None: @@ -248,6 +294,10 @@ def _post_update() -> None: f"vLLM /update_weights failed: {post_error[0]}" ) from post_error[0] + # vLLM >= 0.21: finalize layerwise reload / quantization. + if _VLLM_NEEDS_WEIGHT_UPDATE_LIFECYCLE: + self._post_json("/finish_weight_update", timeout=120) + def update_model_version(self, version: int) -> None: with self._lock: self._model_version = version diff --git a/examples/mini_swe_env/async_grpo/space_app/Dockerfile b/examples/mini_swe_env/async_grpo/space_app/Dockerfile index 43f734e6f..47b5e496f 100644 --- a/examples/mini_swe_env/async_grpo/space_app/Dockerfile +++ b/examples/mini_swe_env/async_grpo/space_app/Dockerfile @@ -1,6 +1,8 @@ -# Pin vLLM to 0.18.1 — TRL AsyncGRPO supports 0.12-0.18.x for NCCL weight sync. -# vLLM 0.19+ has breaking changes in the weight transfer API. -FROM vllm/vllm-openai:v0.18.1 +# vLLM 0.21.0 — supports Qwen3_5ForCausalLM (text-only), transformers >=5.0, +# and the four-phase weight-transfer protocol (init/start/update/finish). +# TRL AsyncGRPO uses vLLM as an external HTTP server; its optional-dep pin +# (<=0.18.0) does not apply when vLLM is installed independently. +FROM vllm/vllm-openai:v0.21.0 # System deps + Docker CLI (for Docker sandbox backend) RUN apt-get update -qq && \ @@ -23,8 +25,13 @@ RUN mkdir -p /home/user/.cache/huggingface && chown -R user:user /home/user/.cac WORKDIR $HOME/app COPY --chown=user . . -# Install deps: force-upgrade huggingface-hub first (resolves vLLM <1.0 vs hf-sandbox >=1.5 conflict) -# vLLM 0.18.1 works fine with newer huggingface-hub at runtime. +# Install deps: +# 1. Force-upgrade huggingface-hub (resolves vLLM vs hf-sandbox conflict). +# 2. Force-install transformers >=5.2.0 (required by TRL v1.4.0 AsyncGRPO, +# and for native Qwen3.5-4B model type support). --no-deps avoids +# pulling vLLM's bundled transformers which may conflict. +# 3. TRL emits a warning about vLLM >0.18 but functions correctly — +# the version check is advisory, not blocking. RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ uv pip install --system --no-cache --no-deps \ -e "." \ @@ -37,7 +44,8 @@ RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ "aiohttp>=3.13.5" \ "fastmcp>=3.3.0" \ "trackio>=0.25.0" \ - "bitsandbytes>=0.43.0" + "bitsandbytes>=0.43.0" && \ + uv pip install --system --no-cache --no-deps "transformers>=5.2.0" # Runtime defaults ENV PYTHONPATH=$HOME/app/src:$HOME/app/envs \ diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index f4d4a4862..73747fcc7 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -182,7 +182,7 @@ for key, value in space_secrets.items(): # Variables (non-sensitive) variables = { "MAX_MODEL_LEN": "4096", - "GPU_MEMORY_UTILIZATION": "0.50", + "GPU_MEMORY_UTILIZATION": "0.70", "VLLM_GPU": "0", "TRAINER_GPU": "1", "INTERCEPTION_HOST": "0.0.0.0", diff --git a/examples/mini_swe_env/async_grpo/space_app/start.sh b/examples/mini_swe_env/async_grpo/space_app/start.sh index 7a4587a3d..696d155f0 100755 --- a/examples/mini_swe_env/async_grpo/space_app/start.sh +++ b/examples/mini_swe_env/async_grpo/space_app/start.sh @@ -45,16 +45,27 @@ echo "========================================" # ── 1. Start vLLM ───────────────────────────────────────────── echo "[start.sh] Starting vLLM on GPU $VLLM_GPU..." # Async GRPO weight sync requires vLLM dev mode + NCCL transfer endpoints. +# Qwen3.5 uses Gated DeltaNet (Mamba-like), which requires one Mamba cache +# block per concurrent sequence. --max-num-seqs 64 prevents exceeding cache. +# vLLM 0.21+ registers Qwen3_5ForCausalLM natively; --language-model-only skips +# vision encoder to save memory. Weight sync uses is_checkpoint_format=True +# which routes through model.load_weights() for proper name remapping. CUDA_VISIBLE_DEVICES="$VLLM_GPU" VLLM_SERVER_DEV_MODE=1 vllm serve "$MODEL" \ --tensor-parallel-size 1 \ --max-model-len "$MAX_MODEL_LEN" \ + --max-num-seqs 64 \ --host 127.0.0.1 \ --port "$VLLM_PORT" \ --api-key "$VLLM_KEY" \ --gpu-memory-utilization "$GPU_MEM_UTIL" \ --logprobs-mode processed_logprobs \ --weight-transfer-config '{"backend":"nccl"}' \ - > /tmp/vllm.log 2>&1 & + --language-model-only \ + --enable-auto-tool-choice \ + --tool-call-parser qwen3_coder \ + --reasoning-parser qwen3 \ + --default-chat-template-kwargs '{"enable_thinking": false}' \ + 2>&1 | tee /tmp/vllm.log & VLLM_PID=$! echo "[start.sh] vLLM PID=$VLLM_PID" @@ -62,7 +73,7 @@ echo "[start.sh] vLLM PID=$VLLM_PID" # ── 2. Wait for vLLM health ─────────────────────────────────── echo "[start.sh] Waiting for vLLM to be ready..." WAITED=0 -MAX_WAIT=300 +MAX_WAIT=600 while [ $WAITED -lt $MAX_WAIT ]; do if curl -sf "http://127.0.0.1:${VLLM_PORT}/health" > /dev/null 2>&1; then echo "[start.sh] vLLM ready after ${WAITED}s" diff --git a/examples/mini_swe_env/test_version_compat.py b/examples/mini_swe_env/test_version_compat.py new file mode 100644 index 000000000..73eb2279d --- /dev/null +++ b/examples/mini_swe_env/test_version_compat.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +"""Gate 0.3 — Version compatibility smoke test for TRL + vLLM + Qwen3.5-4B. + +Validates that the installed combination of TRL, vLLM, and transformers can: + 1. Import TRL's AsyncGRPOTrainer and AsyncGRPOConfig without errors. + 2. Import vLLM's NCCL weight-transfer modules. + 3. Load a Qwen3.5-4B tokenizer via transformers (proves native model support). + 4. (If vLLM server is running) Send a /v1/completions request and get a response. + 5. (If vLLM server is running) Validate weight-transfer endpoints exist. + +Usage: + # Minimal (no GPU / no running vLLM server): + python examples/mini_swe_env/test_version_compat.py + + # Full (requires vLLM serving Qwen3.5-4B on localhost:8000): + VLLM_URL=http://localhost:8000 python examples/mini_swe_env/test_version_compat.py + +Environment variables: + VLLM_URL Base URL for vLLM server (default: http://localhost:8000) + VLLM_API_KEY API key for vLLM server (default: token) + STUDENT_MODEL Model ID for Qwen3.5-4B (default: unsloth/Qwen3.5-4B) +""" + +from __future__ import annotations + +import importlib +import os +import warnings + +# ── Helpers ──────────────────────────────────────────────────────────── + + +def _check(label: str, passed: bool, detail: str = "") -> bool: + status = "✅ PASS" if passed else "❌ FAIL" + suffix = f" ({detail})" if detail else "" + print(f" {status} {label}{suffix}") + return passed + + +def _version(pkg: str) -> str: + try: + mod = importlib.import_module(pkg) + return getattr(mod, "__version__", "unknown") + except ImportError: + return "NOT INSTALLED" + + +# ── Gate checks ──────────────────────────────────────────────────────── + + +def check_imports() -> bool: + """Check 1: TRL AsyncGRPO imports.""" + print("\n[1/5] TRL AsyncGRPO imports") + ok = True + try: + from trl.experimental.async_grpo import ( # noqa: F401 + AsyncGRPOConfig, + AsyncGRPOTrainer, + ) + + ok = _check("AsyncGRPOConfig import", True) and ok + ok = _check("AsyncGRPOTrainer import", True) and ok + except Exception as exc: + ok = _check("TRL AsyncGRPO imports", False, str(exc)) and ok + return ok + + +def check_vllm_modules() -> bool: + """Check 2: vLLM NCCL weight-transfer modules.""" + print("\n[2/5] vLLM weight-transfer modules") + ok = True + try: + from vllm.distributed.weight_transfer.nccl_engine import ( # noqa: F401 + NCCLTrainerSendWeightsArgs, + NCCLWeightTransferEngine, + ) + + ok = _check("NCCLWeightTransferEngine import", True) and ok + ok = _check("NCCLTrainerSendWeightsArgs import", True) and ok + except Exception as exc: + ok = _check("NCCL engine imports", False, str(exc)) and ok + + try: + from vllm.utils.network_utils import get_ip, get_open_port # noqa: F401 + + ok = _check("network_utils import", True) and ok + except Exception as exc: + ok = _check("network_utils import", False, str(exc)) and ok + return ok + + +def check_tokenizer() -> bool: + """Check 3: Qwen3.5-4B tokenizer loads via transformers.""" + print("\n[3/5] Qwen3.5-4B tokenizer (transformers native support)") + model_id = os.environ.get("STUDENT_MODEL", "unsloth/Qwen3.5-4B") + try: + from transformers import AutoTokenizer + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + tok = AutoTokenizer.from_pretrained(model_id, trust_remote_code=False) + return _check( + f"AutoTokenizer.from_pretrained('{model_id}')", + True, + f"vocab_size={tok.vocab_size}", + ) + except Exception as exc: + return _check(f"tokenizer load for {model_id}", False, str(exc)) + + +def check_vllm_completions() -> bool: + """Check 4: vLLM /v1/completions responds (requires running server).""" + print("\n[4/5] vLLM /v1/completions endpoint") + vllm_url = os.environ.get("VLLM_URL", "http://localhost:8000").rstrip("/") + api_key = os.environ.get("VLLM_API_KEY", "token") + model_id = os.environ.get("STUDENT_MODEL", "unsloth/Qwen3.5-4B") + + try: + import requests + + resp = requests.get(f"{vllm_url}/health", timeout=5) + if resp.status_code != 200: + return _check( + "/health", + False, + f"status={resp.status_code} — is vLLM running at {vllm_url}?", + ) + except Exception: + return _check( + "vLLM server reachable", + False, + f"Cannot connect to {vllm_url} — set VLLM_URL or start vLLM. Skipping.", + ) + + try: + import requests + + body = { + "model": model_id, + "prompt": "Hello", + "max_tokens": 8, + "temperature": 0.0, + } + resp = requests.post( + f"{vllm_url}/v1/completions", + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json=body, + timeout=30, + ) + if resp.status_code != 200: + return _check( + "/v1/completions", + False, + f"status={resp.status_code}: {resp.text[:200]}", + ) + data = resp.json() + text = data["choices"][0].get("text", "") + return _check("/v1/completions", True, f"generated {len(text)} chars") + except Exception as exc: + return _check("/v1/completions", False, str(exc)) + + +def check_weight_transfer_endpoints() -> bool: + """Check 5: Weight-transfer HTTP endpoints exist (requires running server).""" + print("\n[5/5] Weight-transfer HTTP endpoints (VLLM_SERVER_DEV_MODE=1 required)") + vllm_url = os.environ.get("VLLM_URL", "http://localhost:8000").rstrip("/") + api_key = os.environ.get("VLLM_API_KEY", "token") + + try: + import requests + + resp = requests.get(f"{vllm_url}/health", timeout=5) + if resp.status_code != 200: + return _check("vLLM reachable", False, "skipping endpoint checks") + except Exception: + return _check("vLLM server reachable", False, "skipping endpoint checks") + + ok = True + headers = {"Authorization": f"Bearer {api_key}"} + + # Check /get_world_size + try: + resp = requests.get(f"{vllm_url}/get_world_size", headers=headers, timeout=5) + ws = resp.json().get("world_size", "?") if resp.status_code == 200 else "?" + ok = ( + _check( + "GET /get_world_size", + resp.status_code == 200, + f"world_size={ws}" + if resp.status_code == 200 + else f"status={resp.status_code}", + ) + and ok + ) + except Exception as exc: + ok = _check("GET /get_world_size", False, str(exc)) and ok + + # Check that /pause and /resume exist (OPTIONS or try POST) + for endpoint in ["/pause", "/resume"]: + try: + # Use POST with no body — will either work or return 422 (missing params) + # but NOT 404 if the endpoint exists. + resp = requests.post( + f"{vllm_url}{endpoint}", + headers=headers, + timeout=5, + params={"mode": "keep"} if endpoint == "/pause" else None, + ) + exists = resp.status_code != 404 + ok = ( + _check( + f"POST {endpoint}", + exists, + f"status={resp.status_code}" if exists else "404 Not Found", + ) + and ok + ) + except Exception as exc: + ok = _check(f"POST {endpoint}", False, str(exc)) and ok + + # Check vLLM >= 0.20 lifecycle endpoints + for endpoint in ["/start_weight_update", "/finish_weight_update"]: + try: + resp = requests.post( + f"{vllm_url}{endpoint}", + headers=headers, + json={} if endpoint == "/start_weight_update" else None, + timeout=5, + ) + exists = resp.status_code != 404 + ok = ( + _check( + f"POST {endpoint} (vLLM >=0.20)", + exists, + f"status={resp.status_code}" if exists else "404 — pre-0.20 vLLM?", + ) + and ok + ) + except Exception as exc: + ok = _check(f"POST {endpoint}", False, str(exc)) and ok + + return ok + + +# ── Main ─────────────────────────────────────────────────────────────── + + +def main() -> int: + print("=" * 60) + print("Gate 0.3 — TRL + vLLM + Transformers Version Compatibility") + print("=" * 60) + + # Print installed versions + print("\nInstalled versions:") + for pkg in ["trl", "vllm", "transformers", "torch", "accelerate"]: + print(f" {pkg:20s} {_version(pkg)}") + + # Run checks + results: list[bool] = [] + results.append(check_imports()) + results.append(check_vllm_modules()) + results.append(check_tokenizer()) + results.append(check_vllm_completions()) + results.append(check_weight_transfer_endpoints()) + + # Summary + passed = sum(results) + total = len(results) + print(f"\n{'=' * 60}") + print(f"Gate 0.3 result: {passed}/{total} check groups passed") + + if all(results): + print("✅ Gate 0.3 PASSED — version combination is compatible") + return 0 + else: + # Checks 4 and 5 (server-dependent) are soft — they require a running vLLM + core_ok = all(results[:3]) + if core_ok: + print( + "⚠️ Gate 0.3 PARTIAL — core imports OK, " + "server-dependent checks skipped/failed" + ) + print( + " Start vLLM with VLLM_SERVER_DEV_MODE=1 and re-run for full validation." + ) + return 0 + else: + print("❌ Gate 0.3 FAILED — fix import/version errors above") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) From 9396bf6311db630442827ad5a507c296beb4e4d3 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 24 May 2026 12:29:57 +0530 Subject: [PATCH 60/79] feat: traj collection --- .../collect_rollouts_best_of_n.py | 798 ++++++++++++++++++ examples/mini_swe_env/deploy_collect_space.sh | 458 ++++++++++ examples/mini_swe_env/trajectory_store.py | 354 ++++++++ 3 files changed, 1610 insertions(+) create mode 100644 examples/mini_swe_env/collect_rollouts_best_of_n.py create mode 100755 examples/mini_swe_env/deploy_collect_space.sh create mode 100644 examples/mini_swe_env/trajectory_store.py diff --git a/examples/mini_swe_env/collect_rollouts_best_of_n.py b/examples/mini_swe_env/collect_rollouts_best_of_n.py new file mode 100644 index 000000000..cb40d88e0 --- /dev/null +++ b/examples/mini_swe_env/collect_rollouts_best_of_n.py @@ -0,0 +1,798 @@ +#!/usr/bin/env python3 +"""Teacher trajectory collection via Best-of-N rollouts. + +Runs a teacher model (e.g. Qwen3.6-27B) through the Pi scaffold on +SWE-Gym tasks, collecting N rollouts per task with full trajectory +capture for downstream SFT distillation. + +Architecture: + HF Sandbox (SWE-Gym image) → Pi agent + → InterceptionServer (this machine, public URL) + → forward to teacher vLLM endpoint + ← response back to Pi + On answer() → host-side grading (FAIL_TO_PASS / PASS_TO_PASS) + +Required env vars: + SWE_LLM_BASE_URL Teacher vLLM endpoint (e.g. https://your-api.com/v1) + SWE_LLM_API_KEY Bearer token for the endpoint + SWE_LLM_MODEL Model name (e.g. Qwen/Qwen3.6-27B) + INTERCEPTION_BASE_URL Public URL where HF sandboxes can reach this machine + INTERCEPTION_AUTH_TOKEN Auth token for interception server + +Optional: + HF_TOKEN HuggingFace token (for sandbox creation) + +Example: + SWE_LLM_BASE_URL=https://your-vllm.com/v1 \ + SWE_LLM_API_KEY=sk-... \ + SWE_LLM_MODEL=Qwen/Qwen3.6-27B \ + INTERCEPTION_BASE_URL=https://your-tunnel.trycloudflare.com \ + INTERCEPTION_AUTH_TOKEN=secret123 \ + PYTHONPATH=src:envs python examples/mini_swe_env/collect_rollouts_best_of_n.py \ + --n-rollouts 4 --max-concurrent 3 --output-dir trajectories/teacher_27b +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import logging +import os +import sys +import time +import uuid +from collections import defaultdict +from pathlib import Path +from typing import Any + +import httpx + +_root = Path(__file__).resolve().parent.parent.parent +for _p in (_root / "src", _root / "envs"): + if str(_p) not in sys.path: + sys.path.insert(0, str(_p)) + +from mini_swe_env.harness import SWEAgentConfig, SWESessionFactory +from mini_swe_env.task_loader_swegym import load_swegym_tasks +from openenv.core.harness.agents.interception_server import InterceptionServer +from openenv.core.harness.sandbox import create_sandbox_backend + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from trajectory_store import TrajectoryRecord, TrajectoryStore + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(name)s %(levelname)s %(message)s", + datefmt="%H:%M:%S", +) +_log = logging.getLogger("collect-teacher") + + +# ── Rate Limiter ─────────────────────────────────────────────────────────── + + +class TokenBucketRateLimiter: + """Async token-bucket rate limiter with exponential backoff on 429s. + + Allows `rate` requests per `per` seconds. On rate-limit hits (429), + applies exponential backoff before retrying. + """ + + def __init__(self, rate: float, per: float = 60.0) -> None: + self._rate = rate + self._per = per + self._tokens = rate + self._last_refill = time.monotonic() + self._lock = asyncio.Lock() + self._backoff_until = 0.0 # monotonic time until which we're backing off + self._consecutive_429s = 0 + + async def acquire(self) -> None: + """Wait until a token is available.""" + while True: + async with self._lock: + now = time.monotonic() + + # Respect backoff + if now < self._backoff_until: + wait = self._backoff_until - now + _log.debug("rate limiter backing off %.1fs", wait) + else: + # Refill tokens + elapsed = now - self._last_refill + self._tokens = min( + self._rate, self._tokens + elapsed * (self._rate / self._per) + ) + self._last_refill = now + + if self._tokens >= 1.0: + self._tokens -= 1.0 + self._consecutive_429s = 0 + return + wait = (1.0 - self._tokens) * (self._per / self._rate) + + await asyncio.sleep(wait) + + def report_429(self) -> None: + """Report a 429 response to trigger exponential backoff.""" + self._consecutive_429s += 1 + backoff = min(2 ** self._consecutive_429s, 120.0) # cap at 2 min + self._backoff_until = time.monotonic() + backoff + _log.warning( + "429 rate limit hit (consecutive=%d), backing off %.1fs", + self._consecutive_429s, + backoff, + ) + + def report_success(self) -> None: + """Report a successful request to reset backoff state.""" + if self._consecutive_429s > 0: + self._consecutive_429s = 0 + _log.debug("rate limiter backoff reset after success") + + +# ── CLI Args ─────────────────────────────────────────────────────────────── + + +def _build_parser() -> argparse.ArgumentParser: + p = argparse.ArgumentParser( + description="Collect Best-of-N teacher trajectories for SFT distillation" + ) + p.add_argument( + "--task-variant", + default="lite", + choices=["lite", "full"], + help="SWE-Gym variant (default: lite, 230 tasks)", + ) + p.add_argument( + "--n-rollouts", + type=int, + default=4, + help="Number of rollouts per task (default: 4)", + ) + p.add_argument( + "--max-concurrent", + type=int, + default=3, + help="Max concurrent rollouts (default: 3)", + ) + p.add_argument( + "--max-turns", + type=int, + default=50, + help="Max agent turns per rollout (default: 50)", + ) + p.add_argument( + "--agent-timeout-s", + type=float, + default=1800.0, + help="Agent timeout in seconds (default: 1800)", + ) + p.add_argument( + "--request-timeout-s", + type=float, + default=180.0, + help="Per-LLM-request timeout (default: 180s)", + ) + p.add_argument( + "--rate-limit", + type=float, + default=30.0, + help="Max LLM requests per minute (default: 30)", + ) + p.add_argument( + "--output-dir", + default="trajectories/teacher_27b", + help="Output directory for trajectories (default: trajectories/teacher_27b)", + ) + p.add_argument( + "--interception-port", + type=int, + default=9090, + help="InterceptionServer port (default: 9090)", + ) + p.add_argument( + "--interception-host", + default="0.0.0.0", + help="InterceptionServer bind host (default: 0.0.0.0)", + ) + p.add_argument( + "--hf-flavor", + default="cpu-basic", + help="HF Sandbox flavor (default: cpu-basic)", + ) + p.add_argument( + "--max-tasks", + type=int, + default=None, + help="Limit to first N tasks (for testing)", + ) + p.add_argument( + "--start-task", + type=int, + default=0, + help="Start from task index (for resuming partial runs)", + ) + p.add_argument( + "--max-retries", + type=int, + default=3, + help="Max retries per rollout on infra failure (default: 3)", + ) + p.add_argument( + "--export-sft", + action="store_true", + help="Export SFT-ready JSONL after collection", + ) + return p + + +# ── Env Var Helpers ──────────────────────────────────────────────────────── + + +def _must_env(name: str) -> str: + value = os.environ.get(name, "").strip() + if not value: + raise RuntimeError(f"Missing required env var: {name}") + return value + + +# ── Single Rollout ───────────────────────────────────────────────────────── + + +async def _run_one_rollout( + *, + gym_task: Any, + rollout_index: int, + factory: SWESessionFactory, + server: InterceptionServer, + client: httpx.AsyncClient, + llm_base_url: str, + llm_api_key: str, + llm_model: str, + interception_base_url: str, + rate_limiter: TokenBucketRateLimiter, + max_turns: int, + request_timeout_s: float, + agent_timeout_s: float, +) -> TrajectoryRecord | None: + """Run one complete rollout, returning a TrajectoryRecord or None on failure.""" + + swe_task = gym_task.to_swe_task() + episode_id = f"collect-{gym_task.instance_id}-r{rollout_index}-{uuid.uuid4().hex[:6]}" + + _log.info( + "rollout_start instance_id=%s rollout=%d episode=%s", + gym_task.instance_id, + rollout_index, + episode_id, + ) + + t0 = time.time() + session = factory.create(task=swe_task, episode_id=episode_id) + + turns = 0 + answer_called = False + answer_bridged = False + messages_captured: list[dict[str, Any]] = [] + tool_calls_log: list[dict[str, Any]] = [] + + try: + while turns < max_turns: + intercept = await session.next_request(timeout_s=agent_timeout_s) + if intercept is None: + _log.info( + "agent_exited instance_id=%s turns=%d", + gym_task.instance_id, + turns, + ) + break + + turns += 1 + + # Capture the request messages + body = intercept.get("body") or {} + req_messages = body.get("messages") or intercept.get("messages") or [] + + # Only capture on first turn (full history) or append new msgs + if turns == 1: + messages_captured = list(req_messages) + + # ── Rate-limited LLM call with exponential backoff ── + await rate_limiter.acquire() + + llm_resp = await _forward_to_llm( + client, + intercept=intercept, + base_url=llm_base_url, + api_key=llm_api_key, + model=llm_model, + timeout_s=request_timeout_s, + rate_limiter=rate_limiter, + ) + + choice0 = (llm_resp.get("choices") or [{}])[0] + msg = choice0.get("message") or {} + tool_calls = msg.get("tool_calls") or [] + + # Capture assistant message + assistant_msg: dict[str, Any] = {"role": "assistant"} + if msg.get("content"): + assistant_msg["content"] = msg["content"] + if tool_calls: + assistant_msg["tool_calls"] = tool_calls + messages_captured.append(assistant_msg) + + # Log tool calls + for tc in tool_calls: + fn = tc.get("function") or {} + tool_calls_log.append( + { + "turn": turns, + "name": fn.get("name", ""), + "arguments": fn.get("arguments", ""), + "id": tc.get("id", ""), + } + ) + + # Check for answer tool call + if any( + (tc.get("function") or {}).get("name") == "answer" + for tc in tool_calls + ): + answer_called = True + + await session.deliver(intercept, llm_resp) + + # Check if host-side answer was bridged + if bool(getattr(session, "answer_called", False)): + answer_called = True + if bool(getattr(session, "answer_bridged", False)): + answer_bridged = True + + if answer_called: + break + + _log.debug( + "turn=%d instance=%s tools=%s", + turns, + gym_task.instance_id, + [fn.get("name", "?") for tc in tool_calls for fn in [tc.get("function") or {}]], + ) + + # ── Grade ───────────────────────────────────────────────── + vr = session.verify(transcript=[]) + reward = float(getattr(vr, "env_reward", 0.0) or 0.0) + resolved = reward >= 1.0 + reward_source = (vr.metrics or {}).get("reward_source", "unknown") + test_outcomes = (vr.artifacts or {}).get("verify_details", {}) + wall_s = round(time.time() - t0, 2) + + _log.info( + "rollout_done instance_id=%s rollout=%d resolved=%s reward=%.2f " + "turns=%d wall_s=%.1f source=%s", + gym_task.instance_id, + rollout_index, + resolved, + reward, + turns, + wall_s, + reward_source, + ) + + # Capture agent log if zero turns (agent never called LLM) + if turns == 0: + try: + artifacts = session.collect_artifacts() + agent_log = artifacts.get("agent_log", "") if isinstance(artifacts, dict) else "" + if isinstance(agent_log, str) and agent_log.strip(): + _log.warning("zero_turns agent_log tail:\n%s", agent_log[-3000:]) + except Exception as log_exc: + _log.warning("failed to collect agent log: %s", log_exc) + + record = TrajectoryRecord( + trajectory_id=episode_id, + instance_id=gym_task.instance_id, + task_id=f"swegym::{gym_task.instance_id}", + repo=gym_task.repo, + teacher_model=llm_model, + rollout_index=rollout_index, + resolved=resolved, + reward=reward, + reward_source=reward_source, + turns=turns, + wall_s=wall_s, + answer_called=answer_called, + tool_calls_count=len(tool_calls_log), + messages=messages_captured, + tool_calls=tool_calls_log, + test_outcomes=test_outcomes if isinstance(test_outcomes, dict) else {}, + metadata={ + "answer_bridged": answer_bridged, + "episode_id": episode_id, + }, + ) + return record + + except Exception as exc: + wall_s = round(time.time() - t0, 2) + _log.error( + "rollout_error instance_id=%s rollout=%d error=%s wall_s=%.1f", + gym_task.instance_id, + rollout_index, + str(exc)[:200], + wall_s, + ) + # Attempt to capture agent log for debugging + try: + artifacts = session.collect_artifacts() + agent_log = artifacts.get("agent_log", "") if isinstance(artifacts, dict) else "" + if isinstance(agent_log, str) and agent_log.strip(): + _log.error("agent_log tail:\n%s", agent_log[-3000:]) + else: + _log.warning("no agent_log captured") + except Exception as log_exc: + _log.warning("failed to collect agent log: %s", log_exc) + return None + finally: + session.close() + + +async def _forward_to_llm( + client: httpx.AsyncClient, + *, + intercept: dict[str, Any], + base_url: str, + api_key: str, + model: str, + timeout_s: float, + rate_limiter: TokenBucketRateLimiter, + max_retries: int = 5, +) -> dict[str, Any]: + """Forward intercepted request to the teacher LLM with retry + backoff.""" + body = dict(intercept.get("body") or {}) + body["model"] = model + # Don't request logprobs for teacher collection (not needed for SFT) + body.pop("logprobs", None) + body.pop("top_logprobs", None) + body.pop("stream", None) + body.pop("stream_options", None) + # Disable thinking mode — get direct content, not reasoning tokens + body.setdefault("chat_template_kwargs", {"enable_thinking": False}) + + for attempt in range(max_retries): + try: + r = await client.post( + f"{base_url.rstrip('/')}/chat/completions", + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json=body, + timeout=timeout_s, + ) + + if r.status_code == 429: + rate_limiter.report_429() + # Wait for backoff then retry + backoff = min(2 ** (attempt + 1), 120.0) + _log.warning( + "LLM 429 (attempt %d/%d), backing off %.1fs", + attempt + 1, + max_retries, + backoff, + ) + await asyncio.sleep(backoff) + # Re-acquire token after backoff + await rate_limiter.acquire() + continue + + if r.status_code != 200: + raise RuntimeError(f"LLM error {r.status_code}: {r.text[:500]}") + + rate_limiter.report_success() + return r.json() + + except httpx.TimeoutException: + if attempt + 1 < max_retries: + backoff = min(2 ** (attempt + 1), 60.0) + _log.warning( + "LLM timeout (attempt %d/%d), retrying in %.1fs", + attempt + 1, + max_retries, + backoff, + ) + await asyncio.sleep(backoff) + continue + raise + + raise RuntimeError(f"LLM request failed after {max_retries} retries (429s)") + + +# ── Task Runner (manages N rollouts for one task) ────────────────────────── + + +async def _collect_task_rollouts( + *, + gym_task: Any, + n_rollouts: int, + existing_count: int, + factory: SWESessionFactory, + server: InterceptionServer, + client: httpx.AsyncClient, + llm_base_url: str, + llm_api_key: str, + llm_model: str, + interception_base_url: str, + rate_limiter: TokenBucketRateLimiter, + max_turns: int, + request_timeout_s: float, + agent_timeout_s: float, + max_retries: int, + semaphore: asyncio.Semaphore, + store: TrajectoryStore, +) -> list[TrajectoryRecord]: + """Collect all N rollouts for a single task, respecting concurrency.""" + records: list[TrajectoryRecord] = [] + start_idx = existing_count + + for rollout_idx in range(start_idx, n_rollouts): + async with semaphore: + record = None + for retry in range(max_retries): + try: + record = await _run_one_rollout( + gym_task=gym_task, + rollout_index=rollout_idx, + factory=factory, + server=server, + client=client, + llm_base_url=llm_base_url, + llm_api_key=llm_api_key, + llm_model=llm_model, + interception_base_url=interception_base_url, + rate_limiter=rate_limiter, + max_turns=max_turns, + request_timeout_s=request_timeout_s, + agent_timeout_s=agent_timeout_s, + ) + if record is not None: + break + except Exception as exc: + backoff = min(2 ** (retry + 1), 60.0) + _log.warning( + "infra_retry instance_id=%s rollout=%d retry=%d/%d " + "error=%s backoff=%.1fs", + gym_task.instance_id, + rollout_idx, + retry + 1, + max_retries, + str(exc)[:150], + backoff, + ) + await asyncio.sleep(backoff) + + if record is not None: + store.append(record) + store.flush() + records.append(record) + else: + _log.error( + "rollout_abandoned instance_id=%s rollout=%d after %d retries", + gym_task.instance_id, + rollout_idx, + max_retries, + ) + + return records + + +# ── Main ─────────────────────────────────────────────────────────────────── + + +async def _run(args: argparse.Namespace) -> int: + llm_base_url = _must_env("SWE_LLM_BASE_URL") + llm_api_key = _must_env("SWE_LLM_API_KEY") + llm_model = _must_env("SWE_LLM_MODEL") + interception_base_url = _must_env("INTERCEPTION_BASE_URL").rstrip("/") + interception_token = _must_env("INTERCEPTION_AUTH_TOKEN") + + # ── Load tasks ──────────────────────────────────────────────── + _log.info("Loading SWE-Gym %s tasks...", args.task_variant) + tasks = load_swegym_tasks(args.task_variant) + _log.info("Loaded %d tasks", len(tasks)) + + # Apply slicing + tasks = tasks[args.start_task:] + if args.max_tasks is not None: + tasks = tasks[: args.max_tasks] + _log.info( + "Will process %d tasks (start=%d, max=%s)", + len(tasks), + args.start_task, + args.max_tasks, + ) + + # ── Load/create trajectory store ────────────────────────────── + store = TrajectoryStore(args.output_dir) + _log.info("Trajectory store: %s (%d existing)", store.filepath, len(store)) + + # Count existing rollouts per task for resume + existing_counts: dict[str, int] = defaultdict(int) + for rec in store.records: + existing_counts[rec.instance_id] += 1 + + tasks_needing_work = [ + t for t in tasks if existing_counts[t.instance_id] < args.n_rollouts + ] + _log.info( + "Tasks needing work: %d / %d (others already have %d rollouts)", + len(tasks_needing_work), + len(tasks), + args.n_rollouts, + ) + + if not tasks_needing_work: + _log.info("All tasks complete! Nothing to do.") + _print_summary(store, args) + return 0 + + # ── Start InterceptionServer ────────────────────────────────── + server = InterceptionServer( + port=args.interception_port, + host=args.interception_host, + secret=interception_token, + tool_name_allowlist={"answer"}, + ) + await server.start() + _log.info( + "InterceptionServer started on %s:%d (public: %s)", + args.interception_host, + server.port, + interception_base_url, + ) + + # ── Create sandbox backend + session factory ────────────────── + backend = create_sandbox_backend("hf", flavor=args.hf_flavor) + cfg = SWEAgentConfig( + base_url=interception_base_url, + api_key=interception_token, + model=llm_model, + agent_timeout_s=args.agent_timeout_s, + ) + factory = SWESessionFactory( + agent="pi", + config=cfg, + sandbox_backend=backend, + mode="interception_gate", + interception_server=server, + interception_base_url=interception_base_url, + ) + + # ── Rate limiter + concurrency ──────────────────────────────── + rate_limiter = TokenBucketRateLimiter(rate=args.rate_limit, per=60.0) + semaphore = asyncio.Semaphore(args.max_concurrent) + + # ── Run collection ──────────────────────────────────────────── + total_start = time.time() + completed_tasks = 0 + total_resolved = 0 + total_rollouts = 0 + + try: + async with httpx.AsyncClient() as client: + # Process tasks with bounded concurrency. + # We launch all tasks as coroutines but the semaphore inside + # _collect_task_rollouts gates actual sandbox creation. + coros = [] + for gym_task in tasks_needing_work: + existing = existing_counts[gym_task.instance_id] + coros.append( + _collect_task_rollouts( + gym_task=gym_task, + n_rollouts=args.n_rollouts, + existing_count=existing, + factory=factory, + server=server, + client=client, + llm_base_url=llm_base_url, + llm_api_key=llm_api_key, + llm_model=llm_model, + interception_base_url=interception_base_url, + rate_limiter=rate_limiter, + max_turns=args.max_turns, + request_timeout_s=args.request_timeout_s, + agent_timeout_s=args.agent_timeout_s, + max_retries=args.max_retries, + semaphore=semaphore, + store=store, + ) + ) + + # Use gather to run tasks — semaphore controls actual concurrency + results = await asyncio.gather(*coros, return_exceptions=True) + + for i, result in enumerate(results): + if isinstance(result, Exception): + _log.error( + "task_failed instance_id=%s error=%s", + tasks_needing_work[i].instance_id, + str(result)[:200], + ) + elif isinstance(result, list): + completed_tasks += 1 + total_rollouts += len(result) + total_resolved += sum(1 for r in result if r.resolved) + + # Periodic progress + if completed_tasks % 10 == 0: + elapsed = time.time() - total_start + _log.info( + "progress: %d/%d tasks done, %d rollouts " + "(%d resolved), %.0fs elapsed", + completed_tasks, + len(tasks_needing_work), + total_rollouts, + total_resolved, + elapsed, + ) + + finally: + await server.stop() + + # ── Final summary ───────────────────────────────────────────── + total_elapsed = time.time() - total_start + _log.info( + "collection_complete tasks=%d rollouts=%d resolved=%d " + "elapsed=%.1fs (%.1f min)", + completed_tasks, + total_rollouts, + total_resolved, + total_elapsed, + total_elapsed / 60, + ) + + _print_summary(store, args) + + # ── Export SFT data if requested ────────────────────────────── + if args.export_sft: + sft_path = Path(args.output_dir) / "sft_export.jsonl" + count = store.export_for_sft(sft_path, resolved_only=True, max_turns=15) + _log.info("Exported %d trajectories for SFT to %s", count, sft_path) + + return 0 + + +def _print_summary(store: TrajectoryStore, args: argparse.Namespace) -> None: + """Print final statistics.""" + print("\n" + "=" * 68) + print("COLLECTION SUMMARY") + print("=" * 68) + print(store.summary()) + + stats = store.pass_at_k_stats(k_values=[1, 4, 8]) + print(f"\n Total tasks attempted: {stats['total_tasks']}") + print(f" Total trajectories: {stats['total_trajectories']}") + print(f" Overall resolve rate: {stats['resolve_rate']*100:.1f}%") + for k in (1, 4, 8): + key = f"pass@{k}" + if key in stats: + print(f" {key}: {stats[key]*100:.1f}%") + print("=" * 68) + + # Write stats to file + stats_path = Path(args.output_dir) / "stats.json" + # Remove per_task for cleaner top-level file + stats_summary = {k: v for k, v in stats.items() if k != "per_task"} + stats_path.write_text(json.dumps(stats_summary, indent=2)) + _log.info("Stats written to %s", stats_path) + + +def main() -> None: + args = _build_parser().parse_args() + raise SystemExit(asyncio.run(_run(args))) + + +if __name__ == "__main__": + main() diff --git a/examples/mini_swe_env/deploy_collect_space.sh b/examples/mini_swe_env/deploy_collect_space.sh new file mode 100755 index 000000000..618b95322 --- /dev/null +++ b/examples/mini_swe_env/deploy_collect_space.sh @@ -0,0 +1,458 @@ +#!/bin/bash +# deploy_collect_space.sh — Deploy teacher trajectory collection to a CPU-only HF Space +# +# This script: +# 1. Creates a CPU-only HF Space +# 2. Configures secrets (LLM API key, interception auth token) +# 3. Prepares a minimal Docker image with the collection script +# 4. Pushes code and triggers build +# 5. The Space runs collect_rollouts_best_of_n.py autonomously +# +# The Space itself IS the interception server — HF sandboxes call back to the +# Space URL directly (no external tunnel needed). +# +# Prerequisites: +# - hf CLI installed and authenticated (hf auth login) +# - HF_TOKEN set or in ~/.cache/huggingface/token +# +# Usage: +# bash examples/mini_swe_env/deploy_collect_space.sh [OPTIONS] +# +# Options: +# --space-id OWNER/NAME Space ID (default: rycerzes/swe-teacher-collect) +# --model MODEL_NAME Model name on vLLM endpoint (default: qwen-3.6-27b) +# --api-url URL vLLM API base URL (default: https://api.siemens.com/llm/v1) +# --api-key KEY vLLM API key (required, or set SWE_LLM_API_KEY) +# --n-rollouts N Rollouts per task (default: 4) +# --max-concurrent N Concurrent rollouts (default: 3) +# --max-turns N Max agent turns (default: 50) +# --max-tasks N Limit tasks for testing (default: all 230 Lite) +# --rate-limit N Max LLM requests/min (default: 30) +# --hardware HW Hardware tier (default: cpu-basic) +# --skip-build Only configure, don't push code +# --monitor Monitor logs after deploy +# --pause Pause the Space +# --resume Resume a paused Space +# +set -euo pipefail + +# ── Defaults ──────────────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +export REPO_ROOT + +SPACE_ID="${HF_SPACE_ID:-rycerzes/swe-teacher-collect}" +MODEL="${SWE_LLM_MODEL:-qwen-3.6-27b}" +API_URL="${SWE_LLM_BASE_URL:-https://api.siemens.com/llm/v1}" +API_KEY="${SWE_LLM_API_KEY:-}" +N_ROLLOUTS=4 +MAX_CONCURRENT=3 +MAX_TURNS=50 +MAX_TASKS="" +RATE_LIMIT=30 +HARDWARE="cpu-basic" +SKIP_BUILD=false +MONITOR=false +PAUSE=false +RESUME=false + +# ── Python detection ──────────────────────────────────────────────────── +if [ -x "$REPO_ROOT/.venv/bin/python" ]; then + PYTHON_CMD=("$REPO_ROOT/.venv/bin/python") +elif command -v uv >/dev/null 2>&1; then + PYTHON_CMD=(uv run python) +elif command -v python3 >/dev/null 2>&1; then + PYTHON_CMD=("$(command -v python3)") +else + echo "ERROR: Python interpreter not found." + exit 1 +fi + +if ! command -v hf >/dev/null 2>&1; then + echo "ERROR: hf CLI not found. Install huggingface_hub CLI and run 'hf auth login'." + exit 1 +fi + +export PYTHONUTF8=1 + +# ── Parse args ────────────────────────────────────────────────────────── +while [[ $# -gt 0 ]]; do + case "$1" in + --space-id) SPACE_ID="$2"; shift 2 ;; + --model) MODEL="$2"; shift 2 ;; + --api-url) API_URL="$2"; shift 2 ;; + --api-key) API_KEY="$2"; shift 2 ;; + --n-rollouts) N_ROLLOUTS="$2"; shift 2 ;; + --max-concurrent) MAX_CONCURRENT="$2"; shift 2 ;; + --max-turns) MAX_TURNS="$2"; shift 2 ;; + --max-tasks) MAX_TASKS="$2"; shift 2 ;; + --rate-limit) RATE_LIMIT="$2"; shift 2 ;; + --hardware) HARDWARE="$2"; shift 2 ;; + --skip-build) SKIP_BUILD=true; shift ;; + --monitor) MONITOR=true; shift ;; + --pause) PAUSE=true; shift ;; + --resume) RESUME=true; shift ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +# ── Validate ──────────────────────────────────────────────────────────── +if [ -z "$API_KEY" ] && [ "$PAUSE" = false ] && [ "$RESUME" = false ]; then + echo "ERROR: API key required. Set SWE_LLM_API_KEY or pass --api-key" + exit 1 +fi + +# ── Resolve HF_TOKEN ──────────────────────────────────────────────────── +if [ -z "${HF_TOKEN:-}" ]; then + if [ -f "$HOME/.cache/huggingface/token" ]; then + HF_TOKEN=$(cat "$HOME/.cache/huggingface/token") + else + echo "ERROR: HF_TOKEN not set and not found in ~/.cache/huggingface/token" + echo "Run: hf auth login" + exit 1 + fi +fi +export HF_TOKEN + +# ── Pause/Resume shortcuts ────────────────────────────────────────────── +if [ "$PAUSE" = true ]; then + echo "⏸ Pausing Space $SPACE_ID..." + "${PYTHON_CMD[@]}" -c " +from huggingface_hub import HfApi +import os +api = HfApi(token=os.environ['HF_TOKEN']) +api.pause_space('$SPACE_ID') +print('Space paused.') +" + exit 0 +fi + +if [ "$RESUME" = true ]; then + echo "▶ Resuming Space $SPACE_ID..." + "${PYTHON_CMD[@]}" -c " +from huggingface_hub import HfApi +import os +api = HfApi(token=os.environ['HF_TOKEN']) +api.restart_space('$SPACE_ID', factory_reboot=True) +print('Space restarted.') +" + exit 0 +fi + +# ── Print config ──────────────────────────────────────────────────────── +echo "╔══════════════════════════════════════════════════════════╗" +echo "║ SWE Teacher Trajectory Collection — HF Space Deploy ║" +echo "╠══════════════════════════════════════════════════════════╣" +printf "║ %-54s ║\n" "Space: $SPACE_ID" +printf "║ %-54s ║\n" "Model: $MODEL" +printf "║ %-54s ║\n" "API URL: $API_URL" +printf "║ %-54s ║\n" "Hardware: $HARDWARE" +printf "║ %-54s ║\n" "N Rollouts: $N_ROLLOUTS" +printf "║ %-54s ║\n" "Concurrent: $MAX_CONCURRENT" +printf "║ %-54s ║\n" "Max Turns: $MAX_TURNS" +printf "║ %-54s ║\n" "Max Tasks: ${MAX_TASKS:-all (230 Lite)}" +printf "║ %-54s ║\n" "Rate Limit: $RATE_LIMIT req/min" +echo "╚══════════════════════════════════════════════════════════╝" +echo "" + +# ── Step 1: Create Space ──────────────────────────────────────────────── +echo "[1/6] Creating Space (if needed)..." +hf repo create "$SPACE_ID" --repo-type space --space-sdk docker --no-private --exist-ok >/dev/null +echo " ✓ Space exists: https://huggingface.co/spaces/$SPACE_ID" + +# ── Step 2: Configure secrets ─────────────────────────────────────────── +echo "[2/6] Configuring secrets and variables..." +"${PYTHON_CMD[@]}" << PYEOF +from huggingface_hub import HfApi +import os, secrets + +api = HfApi(token=os.environ['HF_TOKEN']) +space_id = "$SPACE_ID" + +# Secrets (sensitive) +space_secrets = { + "HF_TOKEN": os.environ["HF_TOKEN"], + "SWE_LLM_API_KEY": "$API_KEY", + "INTERCEPTION_AUTH_TOKEN": secrets.token_urlsafe(32), +} +for key, value in space_secrets.items(): + try: + api.add_space_secret(space_id, key, value) + print(f" ✓ Secret: {key}") + except Exception as e: + print(f" ⚠ Secret {key}: {e}") + +# Variables (non-sensitive) +variables = { + "SWE_LLM_BASE_URL": "$API_URL", + "SWE_LLM_MODEL": "$MODEL", + "N_ROLLOUTS": "$N_ROLLOUTS", + "MAX_CONCURRENT": "$MAX_CONCURRENT", + "MAX_TURNS": "$MAX_TURNS", + "MAX_TASKS": "$MAX_TASKS", + "RATE_LIMIT": "$RATE_LIMIT", + "TASK_VARIANT": "lite", + "HF_SANDBOX_FLAVOR": "cpu-basic", +} +for key, value in variables.items(): + if not value: + continue + try: + api.add_space_variable(space_id, key, value) + print(f" ✓ Var: {key}={value}") + except Exception as e: + print(f" ⚠ Var {key}: {e}") +PYEOF + +# ── Step 3: Set hardware ─────────────────────────────────────────────── +echo "[3/6] Setting hardware to $HARDWARE..." +"${PYTHON_CMD[@]}" -c " +from huggingface_hub import HfApi +import os +api = HfApi(token=os.environ['HF_TOKEN']) +api.request_space_hardware('$SPACE_ID', '$HARDWARE') +print(' ✓ Hardware: $HARDWARE') +" + +# ── Step 4: Prepare staging directory ────────────────────────────────── +if [ "$SKIP_BUILD" = true ]; then + echo "[4/6] Skipping build (--skip-build)" + echo "[5/6] Skipping upload" +else + echo "[4/6] Preparing Space directory..." + STAGE_DIR=$(mktemp -d) + export STAGE_DIR + trap "rm -rf $STAGE_DIR" EXIT + + cd "$REPO_ROOT" + + # Copy essential files + "${PYTHON_CMD[@]}" << 'PYEOF' +import fnmatch +import os +import shutil +from pathlib import Path + +repo = Path(os.environ["REPO_ROOT"]) +stage = Path(os.environ["STAGE_DIR"]) + +def _ignore(patterns): + def _inner(_dir, names): + ignored = set() + for name in names: + for pat in patterns: + if fnmatch.fnmatch(name, pat): + ignored.add(name) + break + return ignored + return _inner + +common_ignores = [ + "__pycache__", "*.pyc", ".venv", ".tox", ".mypy_cache", + ".pytest_cache", ".ruff_cache", "*.egg-info", "uv.lock", +] + +shutil.copytree(repo / "src", stage / "src", dirs_exist_ok=True, ignore=_ignore(common_ignores)) +shutil.copytree(repo / "envs/mini_swe_env", stage / "envs/mini_swe_env", dirs_exist_ok=True, ignore=_ignore(common_ignores)) + +# Only copy the collection-related files from examples +examples_dst = stage / "examples/mini_swe_env" +examples_dst.mkdir(parents=True, exist_ok=True) +for f in ["collect_rollouts_best_of_n.py", "trajectory_store.py"]: + src = repo / "examples/mini_swe_env" / f + if src.exists(): + shutil.copy2(src, examples_dst / f) + +PYEOF + + cp pyproject.toml "$STAGE_DIR/" + [ -f LICENSE ] && cp LICENSE "$STAGE_DIR/" + + # ── Dockerfile (CPU-only, lightweight) ────────────────────────────── + cat > "$STAGE_DIR/Dockerfile" << 'DOCKERFILE' +FROM python:3.12-slim + +WORKDIR /app + +# System deps for sandbox operations +RUN apt-get update -qq && \ + apt-get install -y -qq --no-install-recommends \ + git curl ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Python deps +COPY pyproject.toml . +RUN pip install --no-cache-dir \ + httpx \ + aiohttp \ + datasets \ + huggingface_hub \ + hf-sandbox \ + pydantic && \ + pip install --no-cache-dir -e . || true + +# Copy source +COPY src/ src/ +COPY envs/ envs/ +COPY examples/ examples/ + +# The start script runs the collection +COPY start.sh /app/start.sh +RUN chmod +x /app/start.sh + +EXPOSE 7860 + +CMD ["/app/start.sh"] +DOCKERFILE + + # ── start.sh ──────────────────────────────────────────────────────── + cat > "$STAGE_DIR/start.sh" << 'STARTSH' +#!/bin/bash +# Teacher trajectory collection — runs inside HF Space. +# +# The Space URL is automatically the INTERCEPTION_BASE_URL since the +# InterceptionServer binds to the app port (7860). +set -e + +echo "========================================" +echo "SWE Teacher Trajectory Collection" +echo "Model: ${SWE_LLM_MODEL}" +echo "API URL: ${SWE_LLM_BASE_URL}" +echo "Variant: ${TASK_VARIANT:-lite}" +echo "N Rollouts: ${N_ROLLOUTS:-4}" +echo "Concurrent: ${MAX_CONCURRENT:-3}" +echo "Max Turns: ${MAX_TURNS:-50}" +echo "Max Tasks: ${MAX_TASKS:-all}" +echo "Rate Limit: ${RATE_LIMIT:-30} req/min" +echo "========================================" + +# Resolve INTERCEPTION_BASE_URL from Space environment +if [ -z "${INTERCEPTION_BASE_URL:-}" ]; then + if [ -n "${SPACE_HOST:-}" ]; then + INTERCEPTION_BASE_URL="https://${SPACE_HOST}" + elif [ -n "${SPACE_ID:-}" ]; then + # Convert owner/name → owner-name.hf.space + OWNER="${SPACE_ID%%/*}" + NAME="${SPACE_ID##*/}" + INTERCEPTION_BASE_URL="https://${OWNER}-${NAME}.hf.space" + else + echo "ERROR: Cannot determine INTERCEPTION_BASE_URL." + echo "Set INTERCEPTION_BASE_URL, SPACE_HOST, or SPACE_ID." + exit 1 + fi +fi +export INTERCEPTION_BASE_URL + +echo "Interception URL: ${INTERCEPTION_BASE_URL}" +echo "" + +# Build args +ARGS=( + --task-variant "${TASK_VARIANT:-lite}" + --n-rollouts "${N_ROLLOUTS:-4}" + --max-concurrent "${MAX_CONCURRENT:-3}" + --max-turns "${MAX_TURNS:-50}" + --rate-limit "${RATE_LIMIT:-30}" + --output-dir /app/trajectories + --hf-flavor "${HF_SANDBOX_FLAVOR:-cpu-basic}" + --max-retries 3 + --interception-port 7860 + --interception-host 0.0.0.0 + --agent-timeout-s 1800 + --export-sft +) + +# Optional: limit tasks +if [ -n "${MAX_TASKS:-}" ]; then + ARGS+=(--max-tasks "${MAX_TASKS}") +fi + +echo "Starting collection..." +exec python examples/mini_swe_env/collect_rollouts_best_of_n.py "${ARGS[@]}" +STARTSH + + # ── Space README ──────────────────────────────────────────────────── + cat > "$STAGE_DIR/README.md" << EOF +--- +title: SWE Teacher Trajectory Collection +emoji: 📚 +colorFrom: green +colorTo: blue +sdk: docker +app_port: 7860 +suggested_hardware: $HARDWARE +startup_duration_timeout: 10m +--- + +# SWE Teacher Trajectory Collection + +Collects Best-of-N teacher trajectories from \`$MODEL\` on SWE-Gym Lite (230 tasks). + +- **Model:** \`$MODEL\` via \`$API_URL\` +- **Rollouts/task:** $N_ROLLOUTS +- **Concurrent:** $MAX_CONCURRENT +- **Max turns:** $MAX_TURNS +- **Rate limit:** $RATE_LIMIT req/min + +Trajectories are stored at \`/app/trajectories/\` inside the container. +EOF + + FILE_COUNT=$(find "$STAGE_DIR" -type f | wc -l) + echo " ✓ Staged $FILE_COUNT files" + + # ── Step 5: Upload to Space ──────────────────────────────────────── + echo "[5/6] Uploading to Space..." + cd "$STAGE_DIR" + hf upload "$SPACE_ID" . --repo-type space \ + --commit-message "Deploy: teacher collection $MODEL, $N_ROLLOUTS rollouts, $MAX_CONCURRENT concurrent" 2>&1 | tail -3 + echo " ✓ Upload complete" +fi + +# ── Step 6: Monitor ──────────────────────────────────────────────────── +echo "[6/6] Deployment triggered." +echo "" +echo " 🔗 Space: https://huggingface.co/spaces/$SPACE_ID" +echo " 🔗 Logs: https://huggingface.co/spaces/$SPACE_ID?logs=container" +echo "" + +if [ "$MONITOR" = true ]; then + echo "Monitoring build (Ctrl+C to stop)..." + "${PYTHON_CMD[@]}" << PYEOF +from huggingface_hub import HfApi +import time, os + +api = HfApi(token=os.environ['HF_TOKEN']) +space_id = "$SPACE_ID" +start = time.time() +last_stage = "" + +while True: + info = api.space_info(space_id) + stage = info.runtime.stage if info.runtime else "UNKNOWN" + elapsed = int(time.time() - start) + + if stage != last_stage: + print(f" [{elapsed:>4}s] {last_stage} -> {stage}") + last_stage = stage + + if stage == "RUNNING": + print(f"\n ✅ Space RUNNING after {elapsed}s") + print(f" Logs: https://huggingface.co/spaces/{space_id}?logs=container") + break + elif stage in ("RUNTIME_ERROR", "BUILD_ERROR", "CONFIG_ERROR"): + print(f"\n ❌ Failed: {stage}") + print(f" Check logs: https://huggingface.co/spaces/{space_id}?logs=build") + break + + time.sleep(15) +PYEOF +fi + +echo "" +echo "Done. Useful commands:" +echo " # Pause (stop billing):" +echo " bash $0 --pause" +echo " # Resume:" +echo " bash $0 --resume" +echo " # Download trajectories:" +echo " hf download $SPACE_ID trajectories/ --repo-type space --local-dir ./trajectories_download" diff --git a/examples/mini_swe_env/trajectory_store.py b/examples/mini_swe_env/trajectory_store.py new file mode 100644 index 000000000..99ce3133f --- /dev/null +++ b/examples/mini_swe_env/trajectory_store.py @@ -0,0 +1,354 @@ +"""Trajectory storage and filtering for teacher rollout collection. + +Stores full multi-turn trajectories as JSONL with rich metadata for +downstream SFT/RFT training. + +Storage format (one JSON object per line): + + { + "trajectory_id": "...", + "instance_id": "...", + "task_id": "...", + "repo": "...", + "teacher_model": "Qwen/Qwen3.6-27B", + "rollout_index": 0, # which attempt (0..N-1) for this task + "resolved": true, + "reward": 1.0, + "reward_source": "host_answer_tool", + "turns": 12, + "wall_s": 245.3, + "answer_called": true, + "tool_calls_count": 15, + "messages": [...], # full conversation (system + user + assistant + tool) + "tool_calls": [...], # extracted tool call log + "test_outcomes": {...}, # FAIL_TO_PASS / PASS_TO_PASS results + "timestamp_utc": "2026-05-24T...", + "metadata": {...}, # additional context + } + +Usage:: + + store = TrajectoryStore("trajectories/teacher_27b") + store.append(trajectory_record) + store.flush() + + # Filter resolved trajectories for SFT + resolved = store.filter(resolved=True, max_turns=15) + + # Compute pass@k + stats = store.pass_at_k_stats(k_values=[1, 4, 8]) +""" + +from __future__ import annotations + +import json +import logging +import math +import os +import time +from collections import defaultdict +from dataclasses import asdict, dataclass, field +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Iterator, Sequence + +_log = logging.getLogger(__name__) + + +@dataclass +class TrajectoryRecord: + """One complete rollout trajectory with metadata.""" + + # Identifiers + trajectory_id: str + instance_id: str + task_id: str + repo: str + + # Teacher info + teacher_model: str + + # Rollout metadata + rollout_index: int # which attempt for this task (0..N-1) + resolved: bool + reward: float + reward_source: str + + # Conversation + turns: int + wall_s: float + answer_called: bool + tool_calls_count: int + messages: list[dict[str, Any]] # full conversation history + tool_calls: list[dict[str, Any]] # extracted tool calls log + + # Grading details + test_outcomes: dict[str, Any] = field(default_factory=dict) + + # Timestamps + timestamp_utc: str = "" + + # Catch-all metadata + metadata: dict[str, Any] = field(default_factory=dict) + + def __post_init__(self) -> None: + if not self.timestamp_utc: + self.timestamp_utc = datetime.now(timezone.utc).isoformat() + + def to_dict(self) -> dict[str, Any]: + return asdict(self) + + @classmethod + def from_dict(cls, d: dict[str, Any]) -> "TrajectoryRecord": + # Handle any extra keys gracefully + known_fields = {f.name for f in cls.__dataclass_fields__.values()} + filtered = {k: v for k, v in d.items() if k in known_fields} + return cls(**filtered) + + +class TrajectoryStore: + """JSONL-backed trajectory store with in-memory index. + + Supports: + - Append-only writes (crash-safe with periodic flush) + - Filtering by resolved, turn count, etc. + - pass@k statistics computation + - Loading from existing JSONL files + """ + + def __init__(self, output_dir: str | Path) -> None: + self._dir = Path(output_dir) + self._dir.mkdir(parents=True, exist_ok=True) + self._filepath = self._dir / "trajectories.jsonl" + self._records: list[TrajectoryRecord] = [] + self._pending_writes: list[TrajectoryRecord] = [] + + # Load existing records if file exists + if self._filepath.exists(): + self._load_existing() + + @property + def filepath(self) -> Path: + return self._filepath + + @property + def records(self) -> list[TrajectoryRecord]: + return list(self._records) + + def __len__(self) -> int: + return len(self._records) + + def append(self, record: TrajectoryRecord) -> None: + """Add a trajectory record (buffered, call flush() to persist).""" + self._records.append(record) + self._pending_writes.append(record) + + def flush(self) -> None: + """Write pending records to disk.""" + if not self._pending_writes: + return + with open(self._filepath, "a") as f: + for rec in self._pending_writes: + f.write(json.dumps(rec.to_dict(), ensure_ascii=False) + "\n") + count = len(self._pending_writes) + self._pending_writes = [] + _log.debug("flushed %d trajectories to %s", count, self._filepath) + + def filter( + self, + *, + resolved: bool | None = None, + max_turns: int | None = None, + min_turns: int | None = None, + teacher_model: str | None = None, + instance_ids: set[str] | None = None, + ) -> list[TrajectoryRecord]: + """Filter trajectories by criteria.""" + results = [] + for rec in self._records: + if resolved is not None and rec.resolved != resolved: + continue + if max_turns is not None and rec.turns > max_turns: + continue + if min_turns is not None and rec.turns < min_turns: + continue + if teacher_model is not None and rec.teacher_model != teacher_model: + continue + if instance_ids is not None and rec.instance_id not in instance_ids: + continue + results.append(rec) + return results + + def resolved_trajectories(self, max_turns: int | None = None) -> list[TrajectoryRecord]: + """Convenience: get all resolved trajectories, optionally capped by turn count.""" + return self.filter(resolved=True, max_turns=max_turns) + + def pass_at_k_stats( + self, + k_values: Sequence[int] = (1, 4, 8), + ) -> dict[str, Any]: + """Compute pass@k statistics across all tasks. + + Uses the unbiased estimator: + pass@k = 1 - C(n-c, k) / C(n, k) + where n = total rollouts for the task, c = number resolved. + + Returns dict with per-k rates and per-task details. + """ + # Group by instance_id + by_task: dict[str, list[TrajectoryRecord]] = defaultdict(list) + for rec in self._records: + by_task[rec.instance_id].append(rec) + + stats: dict[str, Any] = { + "total_tasks": len(by_task), + "total_trajectories": len(self._records), + "total_resolved": sum(1 for r in self._records if r.resolved), + "resolve_rate": ( + sum(1 for r in self._records if r.resolved) / len(self._records) + if self._records + else 0.0 + ), + } + + for k in k_values: + pass_rates: list[float] = [] + for instance_id, recs in by_task.items(): + n = len(recs) + c = sum(1 for r in recs if r.resolved) + if n < k: + # Not enough rollouts — use empirical rate + pass_rates.append(1.0 if c > 0 else 0.0) + else: + # Unbiased estimator: pass@k = 1 - C(n-c, k) / C(n, k) + pass_rates.append(_pass_at_k(n, c, k)) + + rate = sum(pass_rates) / len(pass_rates) if pass_rates else 0.0 + stats[f"pass@{k}"] = round(rate, 4) + + # Per-task breakdown + per_task: dict[str, dict[str, Any]] = {} + for instance_id, recs in by_task.items(): + n = len(recs) + c = sum(1 for r in recs if r.resolved) + per_task[instance_id] = { + "n_rollouts": n, + "n_resolved": c, + "resolve_rate": round(c / n, 4) if n > 0 else 0.0, + "avg_turns": round(sum(r.turns for r in recs) / n, 1) if n > 0 else 0, + "avg_wall_s": round(sum(r.wall_s for r in recs) / n, 1) if n > 0 else 0, + } + stats["per_task"] = per_task + + return stats + + def summary(self) -> str: + """Human-readable summary of the store.""" + if not self._records: + return "TrajectoryStore: empty" + + resolved = sum(1 for r in self._records if r.resolved) + n_tasks = len(set(r.instance_id for r in self._records)) + avg_turns = sum(r.turns for r in self._records) / len(self._records) + avg_wall = sum(r.wall_s for r in self._records) / len(self._records) + + lines = [ + f"TrajectoryStore: {self._filepath}", + f" Total trajectories: {len(self._records)}", + f" Unique tasks: {n_tasks}", + f" Resolved: {resolved}/{len(self._records)} ({100*resolved/len(self._records):.1f}%)", + f" Avg turns: {avg_turns:.1f}", + f" Avg wall time: {avg_wall:.1f}s", + ] + + stats = self.pass_at_k_stats() + for k in (1, 4, 8): + key = f"pass@{k}" + if key in stats: + lines.append(f" {key}: {stats[key]*100:.1f}%") + + return "\n".join(lines) + + def export_for_sft( + self, + output_path: str | Path, + *, + resolved_only: bool = True, + max_turns: int | None = 15, + deduplicate: bool = True, + ) -> int: + """Export filtered trajectories in chat-format JSONL for SFT training. + + Each line is: {"messages": [...], "instance_id": "...", "teacher_model": "..."} + + Returns the number of exported trajectories. + """ + candidates = self.filter(resolved=resolved_only, max_turns=max_turns) + + if deduplicate: + # Keep shortest successful trajectory per task + best_per_task: dict[str, TrajectoryRecord] = {} + for rec in candidates: + existing = best_per_task.get(rec.instance_id) + if existing is None or rec.turns < existing.turns: + best_per_task[rec.instance_id] = rec + candidates = list(best_per_task.values()) + + output_path = Path(output_path) + output_path.parent.mkdir(parents=True, exist_ok=True) + + count = 0 + with open(output_path, "w") as f: + for rec in candidates: + entry = { + "messages": rec.messages, + "instance_id": rec.instance_id, + "teacher_model": rec.teacher_model, + "turns": rec.turns, + "trajectory_id": rec.trajectory_id, + } + f.write(json.dumps(entry, ensure_ascii=False) + "\n") + count += 1 + + _log.info("exported %d trajectories for SFT to %s", count, output_path) + return count + + def _load_existing(self) -> None: + """Load records from existing JSONL file.""" + count = 0 + with open(self._filepath) as f: + for line_num, line in enumerate(f, 1): + line = line.strip() + if not line: + continue + try: + d = json.loads(line) + self._records.append(TrajectoryRecord.from_dict(d)) + count += 1 + except (json.JSONDecodeError, TypeError) as exc: + _log.warning("skipping invalid line %d: %s", line_num, exc) + if count: + _log.info("loaded %d existing trajectories from %s", count, self._filepath) + + +def _pass_at_k(n: int, c: int, k: int) -> float: + """Unbiased pass@k estimator. + + pass@k = 1 - C(n-c, k) / C(n, k) + + Uses log-space computation for numerical stability. + """ + if n - c < k: + return 1.0 + if c == 0: + return 0.0 + # log(C(n-c, k)) - log(C(n, k)) + log_num = sum(math.log(n - c - i) for i in range(k)) + log_den = sum(math.log(n - i) for i in range(k)) + return 1.0 - math.exp(log_num - log_den) + + +__all__ = [ + "TrajectoryRecord", + "TrajectoryStore", +] From cc6dacdcf0e2245f709b7686aaf1f072974076ba Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 25 May 2026 11:22:20 +0530 Subject: [PATCH 61/79] chore: rm mention of cf --- examples/mini_swe_env/collect_rollouts_best_of_n.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mini_swe_env/collect_rollouts_best_of_n.py b/examples/mini_swe_env/collect_rollouts_best_of_n.py index cb40d88e0..269ed2e07 100644 --- a/examples/mini_swe_env/collect_rollouts_best_of_n.py +++ b/examples/mini_swe_env/collect_rollouts_best_of_n.py @@ -26,7 +26,7 @@ SWE_LLM_BASE_URL=https://your-vllm.com/v1 \ SWE_LLM_API_KEY=sk-... \ SWE_LLM_MODEL=Qwen/Qwen3.6-27B \ - INTERCEPTION_BASE_URL=https://your-tunnel.trycloudflare.com \ + INTERCEPTION_BASE_URL=https://your-public-url.example.com \ INTERCEPTION_AUTH_TOKEN=secret123 \ PYTHONPATH=src:envs python examples/mini_swe_env/collect_rollouts_best_of_n.py \ --n-rollouts 4 --max-concurrent 3 --output-dir trajectories/teacher_27b From 4e8c4b0a1f1d33366e9d4155c4ca8e1ad7773348 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 25 May 2026 11:52:42 +0530 Subject: [PATCH 62/79] fix: trajectory persistence and periodic uploads --- .../collect_rollouts_best_of_n.py | 23 ++++- examples/mini_swe_env/deploy_collect_space.sh | 15 ++- examples/mini_swe_env/trajectory_store.py | 96 ++++++++++++++++++- 3 files changed, 126 insertions(+), 8 deletions(-) diff --git a/examples/mini_swe_env/collect_rollouts_best_of_n.py b/examples/mini_swe_env/collect_rollouts_best_of_n.py index 269ed2e07..cecdbf131 100644 --- a/examples/mini_swe_env/collect_rollouts_best_of_n.py +++ b/examples/mini_swe_env/collect_rollouts_best_of_n.py @@ -225,6 +225,17 @@ def _build_parser() -> argparse.ArgumentParser: action="store_true", help="Export SFT-ready JSONL after collection", ) + p.add_argument( + "--hub-repo-id", + default=os.environ.get("TRAJECTORY_HUB_REPO", ""), + help="HF Dataset repo for trajectory persistence (default: $TRAJECTORY_HUB_REPO)", + ) + p.add_argument( + "--hub-upload-every", + type=int, + default=5, + help="Upload to Hub every N trajectories (default: 5)", + ) return p @@ -615,8 +626,15 @@ async def _run(args: argparse.Namespace) -> int: ) # ── Load/create trajectory store ────────────────────────────── - store = TrajectoryStore(args.output_dir) + hub_repo = args.hub_repo_id.strip() if args.hub_repo_id else None + store = TrajectoryStore( + args.output_dir, + hub_repo_id=hub_repo or None, + hub_upload_every=args.hub_upload_every, + ) _log.info("Trajectory store: %s (%d existing)", store.filepath, len(store)) + if hub_repo: + _log.info("Hub persistence: %s (every %d)", hub_repo, args.hub_upload_every) # Count existing rollouts per task for resume existing_counts: dict[str, int] = defaultdict(int) @@ -755,6 +773,9 @@ async def _run(args: argparse.Namespace) -> int: _print_summary(store, args) + # ── Final hub upload ─────────────────────────────────────── + store.upload_now() + # ── Export SFT data if requested ────────────────────────────── if args.export_sft: sft_path = Path(args.output_dir) / "sft_export.jsonl" diff --git a/examples/mini_swe_env/deploy_collect_space.sh b/examples/mini_swe_env/deploy_collect_space.sh index 618b95322..df760126b 100755 --- a/examples/mini_swe_env/deploy_collect_space.sh +++ b/examples/mini_swe_env/deploy_collect_space.sh @@ -193,6 +193,8 @@ variables = { "RATE_LIMIT": "$RATE_LIMIT", "TASK_VARIANT": "lite", "HF_SANDBOX_FLAVOR": "cpu-basic", + "TRAJECTORY_HUB_REPO": f"{space_id.split('/')[0]}/swe-teacher-trajectories", + "HUB_UPLOAD_EVERY": "5", } for key, value in variables.items(): if not value: @@ -282,17 +284,17 @@ RUN apt-get update -qq && \ # Python deps COPY pyproject.toml . -RUN pip install --no-cache-dir \ +COPY src/ src/ +RUN pip install --no-cache-dir -e ".[core]" && \ + pip install --no-cache-dir \ httpx \ aiohttp \ datasets \ huggingface_hub \ hf-sandbox \ - pydantic && \ - pip install --no-cache-dir -e . || true + fastmcp -# Copy source -COPY src/ src/ +# Copy remaining source COPY envs/ envs/ COPY examples/ examples/ @@ -360,6 +362,8 @@ ARGS=( --interception-host 0.0.0.0 --agent-timeout-s 1800 --export-sft + --hub-repo-id "${TRAJECTORY_HUB_REPO:-}" + --hub-upload-every "${HUB_UPLOAD_EVERY:-5}" ) # Optional: limit tasks @@ -368,6 +372,7 @@ if [ -n "${MAX_TASKS:-}" ]; then fi echo "Starting collection..." +export PYTHONPATH=/app/src:/app/envs:/app/examples/mini_swe_env exec python examples/mini_swe_env/collect_rollouts_best_of_n.py "${ARGS[@]}" STARTSH diff --git a/examples/mini_swe_env/trajectory_store.py b/examples/mini_swe_env/trajectory_store.py index 99ce3133f..a4f894808 100644 --- a/examples/mini_swe_env/trajectory_store.py +++ b/examples/mini_swe_env/trajectory_store.py @@ -114,15 +114,30 @@ class TrajectoryStore: - Filtering by resolved, turn count, etc. - pass@k statistics computation - Loading from existing JSONL files + - Periodic upload to HF Hub dataset repo (crash-safe persistence) """ - def __init__(self, output_dir: str | Path) -> None: + def __init__( + self, + output_dir: str | Path, + *, + hub_repo_id: str | None = None, + hub_upload_every: int = 5, + ) -> None: self._dir = Path(output_dir) self._dir.mkdir(parents=True, exist_ok=True) self._filepath = self._dir / "trajectories.jsonl" self._records: list[TrajectoryRecord] = [] self._pending_writes: list[TrajectoryRecord] = [] + # HF Hub persistence + self._hub_repo_id = hub_repo_id + self._hub_upload_every = max(1, hub_upload_every) + self._since_last_upload = 0 + self._hub_api: Any = None + if hub_repo_id: + self._init_hub() + # Load existing records if file exists if self._filepath.exists(): self._load_existing() @@ -144,7 +159,7 @@ def append(self, record: TrajectoryRecord) -> None: self._pending_writes.append(record) def flush(self) -> None: - """Write pending records to disk.""" + """Write pending records to disk and upload to Hub if configured.""" if not self._pending_writes: return with open(self._filepath, "a") as f: @@ -154,6 +169,83 @@ def flush(self) -> None: self._pending_writes = [] _log.debug("flushed %d trajectories to %s", count, self._filepath) + # Periodic upload to HF Hub + if self._hub_repo_id: + self._since_last_upload += count + if self._since_last_upload >= self._hub_upload_every: + self._upload_to_hub() + self._since_last_upload = 0 + + def upload_now(self) -> None: + """Force an immediate upload to HF Hub (call at end of run).""" + if self._hub_repo_id: + self._upload_to_hub() + + def _init_hub(self) -> None: + """Initialize HF Hub repo for trajectory persistence.""" + try: + from huggingface_hub import HfApi + self._hub_api = HfApi() + self._hub_api.create_repo( + repo_id=self._hub_repo_id, + repo_type="dataset", + private=True, + exist_ok=True, + ) + _log.info("hub persistence enabled: %s", self._hub_repo_id) + + # Try to download existing trajectories on startup (resume support) + try: + from huggingface_hub import hf_hub_download + local_path = hf_hub_download( + repo_id=self._hub_repo_id, + filename="trajectories.jsonl", + repo_type="dataset", + local_dir=str(self._dir), + ) + _log.info("downloaded existing trajectories from hub") + except Exception: + _log.debug("no existing trajectories on hub (fresh start)") + + except ImportError: + _log.warning("huggingface_hub not installed, hub persistence disabled") + self._hub_repo_id = None + except Exception as exc: + _log.warning("hub init failed: %s (persistence disabled)", exc) + self._hub_repo_id = None + + def _upload_to_hub(self) -> None: + """Upload trajectories.jsonl + stats.json to HF Hub.""" + if not self._hub_api or not self._filepath.exists(): + return + try: + self._hub_api.upload_file( + path_or_fileobj=str(self._filepath), + path_in_repo="trajectories.jsonl", + repo_id=self._hub_repo_id, + repo_type="dataset", + commit_message=f"Update trajectories ({len(self._records)} total)", + ) + # Also upload stats + stats_path = self._dir / "stats.json" + stats = self.pass_at_k_stats() + stats_summary = {k: v for k, v in stats.items() if k != "per_task"} + stats_path.write_text(json.dumps(stats_summary, indent=2)) + self._hub_api.upload_file( + path_or_fileobj=str(stats_path), + path_in_repo="stats.json", + repo_id=self._hub_repo_id, + repo_type="dataset", + commit_message=f"Update stats ({len(self._records)} trajectories)", + ) + _log.info( + "hub_upload complete: %d trajectories to %s", + len(self._records), + self._hub_repo_id, + ) + except Exception as exc: + _log.warning("hub upload failed (will retry next flush): %s", exc) + def filter( self, *, From 131edf321bf9cc2f393c3ffe172062ad69c791cf Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 25 May 2026 14:34:28 +0530 Subject: [PATCH 63/79] fix: rollout handling --- .../collect_rollouts_best_of_n.py | 52 ++++++++++++++++--- examples/mini_swe_env/deploy_collect_space.sh | 6 +++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/examples/mini_swe_env/collect_rollouts_best_of_n.py b/examples/mini_swe_env/collect_rollouts_best_of_n.py index cecdbf131..8835b659a 100644 --- a/examples/mini_swe_env/collect_rollouts_best_of_n.py +++ b/examples/mini_swe_env/collect_rollouts_best_of_n.py @@ -354,13 +354,53 @@ async def _run_one_rollout( ): answer_called = True - await session.deliver(intercept, llm_resp) + # Invoke host-side grading directly (like run_swe_sample.py) + # Pi can't call the tool endpoint itself after we break. + rollout_id = intercept.get("rollout_id", "") + try: + answer_resp = await client.post( + f"http://127.0.0.1:{server.port}/rollout/{rollout_id}/v1/tools/answer", + headers={"Authorization": f"Bearer {server.secret}"}, + json={"arguments": {}}, + timeout=300.0, + ) + if answer_resp.status_code == 200: + answer_bridged = True + _log.info( + "answer_bridged instance_id=%s result=%s", + gym_task.instance_id, + answer_resp.text[:200], + ) + else: + _log.warning( + "answer_bridge_failed instance_id=%s status=%d", + gym_task.instance_id, + answer_resp.status_code, + ) + except Exception as exc: + _log.warning( + "answer_bridge_error instance_id=%s: %s", + gym_task.instance_id, + str(exc)[:150], + ) - # Check if host-side answer was bridged - if bool(getattr(session, "answer_called", False)): - answer_called = True - if bool(getattr(session, "answer_bridged", False)): - answer_bridged = True + # Replace tool-call response with plain text so Pi stops cleanly + llm_resp = dict(llm_resp) + choices = list(llm_resp.get("choices") or []) + if choices: + choice0_mod = dict(choices[0]) + msg_mod = dict(choice0_mod.get("message") or {}) + msg_mod.pop("tool_calls", None) + msg_mod["content"] = ( + (msg_mod.get("content") or "") + + "\nSubmission received and graded on host." + ).strip() + choice0_mod["message"] = msg_mod + choice0_mod["finish_reason"] = "stop" + choices[0] = choice0_mod + llm_resp["choices"] = choices + + await session.deliver(intercept, llm_resp) if answer_called: break diff --git a/examples/mini_swe_env/deploy_collect_space.sh b/examples/mini_swe_env/deploy_collect_space.sh index df760126b..4cf241999 100755 --- a/examples/mini_swe_env/deploy_collect_space.sh +++ b/examples/mini_swe_env/deploy_collect_space.sh @@ -198,6 +198,12 @@ variables = { } for key, value in variables.items(): if not value: + # Remove variable if empty (e.g. MAX_TASKS not set = use all tasks) + try: + api.delete_space_variable(space_id, key) + print(f" ✓ Var removed: {key} (using default)") + except Exception: + pass continue try: api.add_space_variable(space_id, key, value) From afc6c791e445dd433818e7f34c0dedb450900c68 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 29 May 2026 09:32:22 +0530 Subject: [PATCH 64/79] feat: per-turn completion records for RL reconstruction --- .../collect_rollouts_best_of_n.py | 57 +++++++++++++++++-- examples/mini_swe_env/trajectory_store.py | 4 ++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/examples/mini_swe_env/collect_rollouts_best_of_n.py b/examples/mini_swe_env/collect_rollouts_best_of_n.py index 8835b659a..2971a00c4 100644 --- a/examples/mini_swe_env/collect_rollouts_best_of_n.py +++ b/examples/mini_swe_env/collect_rollouts_best_of_n.py @@ -288,6 +288,7 @@ async def _run_one_rollout( answer_bridged = False messages_captured: list[dict[str, Any]] = [] tool_calls_log: list[dict[str, Any]] = [] + completions: list[dict[str, Any]] = [] # Per-turn completion records (Polar-style) try: while turns < max_turns: @@ -306,9 +307,11 @@ async def _run_one_rollout( body = intercept.get("body") or {} req_messages = body.get("messages") or intercept.get("messages") or [] - # Only capture on first turn (full history) or append new msgs - if turns == 1: - messages_captured = list(req_messages) + # Each intercept contains the full conversation so far + # (system + user + assistant + tool results from all previous turns). + # Always update to the latest — on the final turn this gives us + # the complete multi-turn trajectory. + messages_captured = list(req_messages) # ── Rate-limited LLM call with exponential backoff ── await rate_limiter.acquire() @@ -327,6 +330,19 @@ async def _run_one_rollout( msg = choice0.get("message") or {} tool_calls = msg.get("tool_calls") or [] + # Store per-completion record (Polar-compatible format) + # Captures the full request + response per turn for RL reconstruction + completions.append({ + "completion_id": f"{episode_id}-t{turns}", + "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + "request": { + "messages": list(req_messages), + "tools": body.get("tools") or intercept.get("tools") or [], + "model": llm_model, + }, + "response": llm_resp, + }) + # Capture assistant message assistant_msg: dict[str, Any] = {"role": "assistant"} if msg.get("content"): @@ -413,6 +429,34 @@ async def _run_one_rollout( ) # ── Grade ───────────────────────────────────────────────── + # If answer wasn't called during the rollout, invoke grading + # post-hoc on whatever repo state the agent left behind. + # This matches Polar's approach: grade the patch regardless of + # whether the agent explicitly submitted. + if not answer_called: + rollout_id = episode_id + try: + answer_resp = await client.post( + f"http://127.0.0.1:{server.port}/rollout/{rollout_id}/v1/tools/answer", + headers={"Authorization": f"Bearer {server.secret}"}, + json={"arguments": {}}, + timeout=300.0, + ) + if answer_resp.status_code == 200: + answer_bridged = True + _log.info( + "post_hoc_grade instance_id=%s turns=%d result=%s", + gym_task.instance_id, + turns, + answer_resp.text[:200], + ) + except Exception as exc: + _log.warning( + "post_hoc_grade_error instance_id=%s: %s", + gym_task.instance_id, + str(exc)[:150], + ) + vr = session.verify(transcript=[]) reward = float(getattr(vr, "env_reward", 0.0) or 0.0) resolved = reward >= 1.0 @@ -458,6 +502,7 @@ async def _run_one_rollout( tool_calls_count=len(tool_calls_log), messages=messages_captured, tool_calls=tool_calls_log, + completions=completions, test_outcomes=test_outcomes if isinstance(test_outcomes, dict) else {}, metadata={ "answer_bridged": answer_bridged, @@ -504,9 +549,9 @@ async def _forward_to_llm( """Forward intercepted request to the teacher LLM with retry + backoff.""" body = dict(intercept.get("body") or {}) body["model"] = model - # Don't request logprobs for teacher collection (not needed for SFT) - body.pop("logprobs", None) - body.pop("top_logprobs", None) + # Request logprobs for RL-readiness (token-faithful reconstruction) + body["logprobs"] = True + body["top_logprobs"] = 1 body.pop("stream", None) body.pop("stream_options", None) # Disable thinking mode — get direct content, not reasoning tokens diff --git a/examples/mini_swe_env/trajectory_store.py b/examples/mini_swe_env/trajectory_store.py index a4f894808..44a424567 100644 --- a/examples/mini_swe_env/trajectory_store.py +++ b/examples/mini_swe_env/trajectory_store.py @@ -85,6 +85,10 @@ class TrajectoryRecord: # Grading details test_outcomes: dict[str, Any] = field(default_factory=dict) + # Per-turn completion records (Polar-compatible) + # Each entry: {completion_id, timestamp, request: {messages, tools, model}, response: {...}} + completions: list[dict[str, Any]] = field(default_factory=list) + # Timestamps timestamp_utc: str = "" From d0659e2ace0bf0fd75c4677dceb3334045a88c8e Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 29 May 2026 09:54:14 +0530 Subject: [PATCH 65/79] feat: post-hoc grading & evaluation --- envs/mini_swe_env/harness.py | 44 ++++++++++++++++--- .../collect_rollouts_best_of_n.py | 35 +++------------ examples/mini_swe_env/trajectory_store.py | 2 +- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index e00255d1f..f04911fff 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -173,14 +173,15 @@ class SWESession(CLIAgentSession): Extends :class:`CLIAgentSession` with: - ``verify()`` — returns the reward produced by the host-side - ``answer`` tool (stored by the InterceptionServer tool handler). - - Falls back to running verify commands for legacy tasks. + ``answer`` tool, or grades the repo state post-hoc if no answer + was called. - SWE task metadata. - **Reward architecture**: The ``answer`` tool runs host-side via - the InterceptionServer's ``/vf/tools`` routing. ``verify()`` simply - returns the reward already computed during the rollout. There is - no separate grading step. + **Reward architecture**: The ``answer`` tool is optional. If the + agent calls it, grading runs immediately and returns feedback. + If the agent never calls ``answer()`` (timeout, max turns, natural + exit), ``verify()`` grades the repo state post-hoc — the agent's + edits are evaluated regardless of explicit submission. """ def __init__( @@ -188,6 +189,7 @@ def __init__( *, swe_task: SWETask, verify_timeout_s: int = VERIFY_TIMEOUT_S, + grade_fn: Any | None = None, **kwargs: Any, ) -> None: super().__init__(**kwargs) @@ -197,6 +199,8 @@ def __init__( self._answer_reward_source: str | None = None self._answer_called = False self._answer_bridged = False + # Callback for post-hoc grading: (sandbox, swe_task) -> (reward, resolved) + self._grade_fn = grade_fn @property def swe_task(self) -> SWETask: @@ -332,7 +336,32 @@ def verify( }, ) - # 4. No reward source — agent didn't call answer, no verify cmds. + # 4. Post-hoc grading: grade the repo state regardless of answer(). + # whether or not it explicitly submitted. + if self._grade_fn is not None: + try: + reward, resolved = self._grade_fn(self.sandbox, self._swe_task) + source = "post_hoc_grade" + return VerifyResult( + env_reward=float(reward), + done=True, + metrics={ + "instance_id": self._swe_task.instance_id, + "reward_source": source, + "resolved": resolved, + "answer_called": False, + "answer_bridged": False, + }, + artifacts={ + "task_id": self._swe_task.task_id, + }, + ) + except Exception: + _log.exception( + "post-hoc grading failed for %s", self._swe_task.instance_id + ) + + # 5. No grading possible (no grade_fn, no verify cmds, no answer). return VerifyResult( env_reward=0.0, done=True, @@ -524,6 +553,7 @@ def create( session = SWESession( swe_task=swe_task, verify_timeout_s=self._verify_timeout_s, + grade_fn=self._grade_answer_submission, spec=self._spec, sandbox=sandbox, task=agent_task, diff --git a/examples/mini_swe_env/collect_rollouts_best_of_n.py b/examples/mini_swe_env/collect_rollouts_best_of_n.py index 2971a00c4..20e1fe441 100644 --- a/examples/mini_swe_env/collect_rollouts_best_of_n.py +++ b/examples/mini_swe_env/collect_rollouts_best_of_n.py @@ -288,7 +288,7 @@ async def _run_one_rollout( answer_bridged = False messages_captured: list[dict[str, Any]] = [] tool_calls_log: list[dict[str, Any]] = [] - completions: list[dict[str, Any]] = [] # Per-turn completion records (Polar-style) + completions: list[dict[str, Any]] = [] # Per-turn completion records try: while turns < max_turns: @@ -330,7 +330,7 @@ async def _run_one_rollout( msg = choice0.get("message") or {} tool_calls = msg.get("tool_calls") or [] - # Store per-completion record (Polar-compatible format) + # Store per-completion record # Captures the full request + response per turn for RL reconstruction completions.append({ "completion_id": f"{episode_id}-t{turns}", @@ -429,34 +429,9 @@ async def _run_one_rollout( ) # ── Grade ───────────────────────────────────────────────── - # If answer wasn't called during the rollout, invoke grading - # post-hoc on whatever repo state the agent left behind. - # This matches Polar's approach: grade the patch regardless of - # whether the agent explicitly submitted. - if not answer_called: - rollout_id = episode_id - try: - answer_resp = await client.post( - f"http://127.0.0.1:{server.port}/rollout/{rollout_id}/v1/tools/answer", - headers={"Authorization": f"Bearer {server.secret}"}, - json={"arguments": {}}, - timeout=300.0, - ) - if answer_resp.status_code == 200: - answer_bridged = True - _log.info( - "post_hoc_grade instance_id=%s turns=%d result=%s", - gym_task.instance_id, - turns, - answer_resp.text[:200], - ) - except Exception as exc: - _log.warning( - "post_hoc_grade_error instance_id=%s: %s", - gym_task.instance_id, - str(exc)[:150], - ) - + # session.verify() handles both cases: + # - answer was called → uses stored reward from host_answer_tool + # - answer not called → runs post-hoc grading via grade_fn vr = session.verify(transcript=[]) reward = float(getattr(vr, "env_reward", 0.0) or 0.0) resolved = reward >= 1.0 diff --git a/examples/mini_swe_env/trajectory_store.py b/examples/mini_swe_env/trajectory_store.py index 44a424567..6c322e63e 100644 --- a/examples/mini_swe_env/trajectory_store.py +++ b/examples/mini_swe_env/trajectory_store.py @@ -85,7 +85,7 @@ class TrajectoryRecord: # Grading details test_outcomes: dict[str, Any] = field(default_factory=dict) - # Per-turn completion records (Polar-compatible) + # Per-turn completion records # Each entry: {completion_id, timestamp, request: {messages, tools, model}, response: {...}} completions: list[dict[str, Any]] = field(default_factory=list) From b9ca3b196a7a9daf16e340710db61c8c512d4fd0 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 29 May 2026 10:00:57 +0530 Subject: [PATCH 66/79] fix: allow unlimited agent turns --- examples/mini_swe_env/collect_rollouts_best_of_n.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/mini_swe_env/collect_rollouts_best_of_n.py b/examples/mini_swe_env/collect_rollouts_best_of_n.py index 20e1fe441..a6e43335f 100644 --- a/examples/mini_swe_env/collect_rollouts_best_of_n.py +++ b/examples/mini_swe_env/collect_rollouts_best_of_n.py @@ -160,8 +160,8 @@ def _build_parser() -> argparse.ArgumentParser: p.add_argument( "--max-turns", type=int, - default=50, - help="Max agent turns per rollout (default: 50)", + default=0, + help="Max agent turns per rollout (default: 0 = unlimited, agent runs until exit or timeout)", ) p.add_argument( "--agent-timeout-s", @@ -291,7 +291,7 @@ async def _run_one_rollout( completions: list[dict[str, Any]] = [] # Per-turn completion records try: - while turns < max_turns: + while max_turns == 0 or turns < max_turns: intercept = await session.next_request(timeout_s=agent_timeout_s) if intercept is None: _log.info( From 212dd474cb18c25a59e3f666b320710f353a8f43 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Fri, 29 May 2026 20:01:03 +0530 Subject: [PATCH 67/79] fix: group-relative advantage with num_generations parameter --- .../mini_swe_env/async_grpo/rollout_worker.py | 87 +++++++++++++++++-- examples/mini_swe_env/train_swe_async_grpo.py | 13 ++- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index f46cd4df8..5f6c7a3e6 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -106,6 +106,9 @@ class WorkerConfig: max_turns: int = 50 max_completion_tokens: int = 2048 temperature: float = 1.0 + # Number of rollouts per prompt for group-relative advantage + # normalization (true GRPO). Polar used 16. + num_generations: int = 16 # After returning a terminal plain-text response (finish_reason=stop, # no tool_calls), wait briefly for a follow-up request before treating # the rollout as complete. This avoids 600s stalls when agent exit @@ -163,6 +166,14 @@ def __init__( self._started = False self._model_update_group: Any | None = None + # ── Group-relative advantage (GRPO) ────────────────────── + # Accumulate rollout samples by task until num_generations are + # collected, then normalize advantages within the group and push + # all samples to the rollout buffer. + self._group_lock = threading.Lock() + # task_id → list of (RolloutSample with raw reward in .advantage) + self._pending_groups: dict[str, list[RolloutSample]] = {} + # Prefix to prepend to trainer parameter names so they match vLLM's # model architecture. For VLM models like Qwen3_5ForConditionalGeneration # served with --language-model-only, vLLM parameters are at @@ -413,6 +424,75 @@ def _model_ver(self) -> int: with self._lock: return self._model_version + def _next_group_task(self) -> SWETask: + """Pick a task that needs more rollouts to complete its group. + + Prioritizes tasks that already have partial groups (some rollouts + done but < num_generations). Falls back to a fresh task. + """ + with self._group_lock: + # Find a task with a partial group that needs more rollouts + for task_id, samples in self._pending_groups.items(): + if len(samples) < self._cfg.num_generations: + # Return the corresponding task + for t in self._tasks: + if t.instance_id == task_id: + return t + # No partial groups — start a fresh task + return self._next_task() + + def _submit_to_group(self, task: SWETask, sample: RolloutSample) -> None: + """Add a completed rollout to its task's group. + + When the group reaches num_generations, normalize advantages + (GRPO-style) and push all samples to the rollout buffer. + """ + completed_group: list[RolloutSample] | None = None + + with self._group_lock: + group = self._pending_groups.setdefault(task.instance_id, []) + group.append(sample) + + if len(group) >= self._cfg.num_generations: + completed_group = self._pending_groups.pop(task.instance_id) + + if completed_group is not None: + self._score_and_push_group(task.instance_id, completed_group) + + def _score_and_push_group( + self, task_id: str, group: list[RolloutSample] + ) -> None: + """Compute group-relative advantages (GRPO) and push to buffer.""" + rewards = [s.advantage for s in group] # raw rewards stored here + n = len(rewards) + mean_r = sum(rewards) / n + var_r = sum((r - mean_r) ** 2 for r in rewards) / n + std_r = var_r**0.5 + + for i, sample in enumerate(group): + if std_r > 1e-8: + sample.advantage = (rewards[i] - mean_r) / std_r + else: + # All rewards identical (e.g. all 0 or all 1) — zero advantage + sample.advantage = 0.0 + + # Add group metrics + sample.metrics["group_reward_mean"] = mean_r + sample.metrics["group_reward_std"] = std_r + sample.metrics["group_size"] = float(n) + + try: + self.rollout_buffer.put(sample, timeout=2.0) + except queue.Full: + _log.warning( + "queue full, dropping sample from group %s", task_id + ) + + _log.info( + "group complete: task=%s n=%d reward_mean=%.3f reward_std=%.3f", + task_id, n, mean_r, std_r, + ) + def _loop(self, idx: int) -> None: while not self._stop.is_set(): while self._pause.is_set() and not self._stop.is_set(): @@ -420,7 +500,7 @@ def _loop(self, idx: int) -> None: if self._stop.is_set(): return - task = self._next_task() + task = self._next_group_task() eid = f"swe-{idx}-{uuid.uuid4().hex[:8]}" try: sample = asyncio.run(self._rollout(task, eid)) @@ -433,10 +513,7 @@ def _loop(self, idx: int) -> None: time.sleep(self._cfg.idle_backoff_s) continue - try: - self.rollout_buffer.put(sample, timeout=2.0) - except queue.Full: - _log.warning("queue full, dropping %s", task.instance_id) + self._submit_to_group(task, sample) # ── Single rollout ───────────────────────────────────────────── diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 6b6a204a3..1df553234 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -118,6 +118,10 @@ def _args() -> argparse.Namespace: p.add_argument("--sandbox-backend", default="hf", choices=["docker", "e2b", "hf"]) p.add_argument("--vllm-url", default="http://localhost:8000") p.add_argument("--agent", default="pi", choices=["pi", "opencode"]) + p.add_argument( + "--num-generations", type=int, default=16, + help="Rollouts per prompt for group-relative advantage (GRPO). Polar used 16.", + ) return p.parse_args() @@ -289,6 +293,7 @@ def main() -> int: config=WorkerConfig( max_inflight=2, max_turns=args.max_turns, + num_generations=args.num_generations, ), ) @@ -309,10 +314,14 @@ def _noop_reward(**kwargs: Any) -> list[float]: "vllm_server_timeout": 2400.0, "max_completion_length": 2048, "max_steps": args.max_steps, + # Polar: rollout_batch_size=4. With single GPU trainer, + # batch_size=4 via gradient accumulation. "per_device_train_batch_size": 1, - "gradient_accumulation_steps": 1, - "num_generations": 1, + "gradient_accumulation_steps": 4, + "num_generations": args.num_generations, + # Polar: lr=1e-6, weight_decay=0.1 "learning_rate": 1e-6, + "weight_decay": 0.1, "temperature": 1.0, "optim": "adamw_bnb_8bit", "bf16": True, From 9e3c3a707f46722ee581755ffd2a59079ccf0e2c Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 01:50:57 +0530 Subject: [PATCH 68/79] fix: update vLLM weight prefix handling for Qwen3.5 models --- examples/mini_swe_env/async_grpo/rollout_worker.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index 5f6c7a3e6..521d70ea5 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -179,7 +179,15 @@ def __init__( # served with --language-model-only, vLLM parameters are at # "language_model.model.layers.X" but the trainer (AutoModelForCausalLM) # produces "model.layers.X". Set to "" to disable remapping. - self._vllm_weight_prefix = "language_model." + # + # Only Qwen3.5 models need this prefix — they are unified VLMs where + # vLLM resolves Qwen3_5ForConditionalGeneration even in text-only mode. + # Standard CausalLM models (Qwen3, Llama, etc.) have matching param names. + self._vllm_weight_prefix = ( + "language_model." + if "qwen3.5" in vllm_model.lower() or "qwen3_5" in vllm_model.lower() + else "" + ) self._init_weight_transfer() From 49765f7f3846868e69c4bc2f6db710141e37dd4d Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 01:55:50 +0530 Subject: [PATCH 69/79] fix: add num_generations parameter to deploy_hf_space.sh - update deps and envs for named-tunnels --- .../async_grpo/space_app/Dockerfile | 6 ++-- .../async_grpo/space_app/deploy_hf_space.sh | 17 +++++++++-- examples/mini_swe_env/deploy_collect_space.sh | 30 ++++++++++++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/space_app/Dockerfile b/examples/mini_swe_env/async_grpo/space_app/Dockerfile index 47b5e496f..e800b3680 100644 --- a/examples/mini_swe_env/async_grpo/space_app/Dockerfile +++ b/examples/mini_swe_env/async_grpo/space_app/Dockerfile @@ -27,7 +27,7 @@ COPY --chown=user . . # Install deps: # 1. Force-upgrade huggingface-hub (resolves vLLM vs hf-sandbox conflict). -# 2. Force-install transformers >=5.2.0 (required by TRL v1.4.0 AsyncGRPO, +# 2. Force-install transformers >=5.2.0 (required by TRL v1.6.0 AsyncGRPO, # and for native Qwen3.5-4B model type support). --no-deps avoids # pulling vLLM's bundled transformers which may conflict. # 3. TRL emits a warning about vLLM >0.18 but functions correctly — @@ -37,9 +37,9 @@ RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ -e "." \ -e "envs/mini_swe_env" && \ uv pip install --system --no-cache \ - "trl>=1.4.0" \ + "trl>=1.6.0" \ "accelerate>=1.13.0" \ - "hf-sandbox>=0.1.1" \ + "hf-sandbox[named-tunnels] @ git+https://github.com/rycerzes/hf-sandbox@feat/named-tunnels" \ "datasets>=4.8.0" \ "aiohttp>=3.13.5" \ "fastmcp>=3.3.0" \ diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index 73747fcc7..c8634fc18 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -41,6 +41,7 @@ MODEL="${SWE_MODEL:-Qwen/Qwen3-1.7B}" MAX_TASKS=5 MAX_STEPS=10 MAX_TURNS=30 +NUM_GENERATIONS=16 HARDWARE="a10g-largex2" SANDBOX_BACKEND="hf" SKIP_BUILD=false @@ -84,6 +85,7 @@ while [[ $# -gt 0 ]]; do --max-tasks) MAX_TASKS="$2"; shift 2 ;; --max-steps) MAX_STEPS="$2"; shift 2 ;; --max-turns) MAX_TURNS="$2"; shift 2 ;; + --num-generations) NUM_GENERATIONS="$2"; shift 2 ;; --hardware) HARDWARE="$2"; shift 2 ;; --sandbox-backend) SANDBOX_BACKEND="$2"; shift 2 ;; --skip-build) SKIP_BUILD=true; shift ;; @@ -172,6 +174,17 @@ space_secrets = { "SWE_MODEL": "$MODEL", "VLLM_API_KEY": "token", } + +# Cloudflare named tunnel secrets (optional — enables reliable sandbox connectivity) +cf_secrets = { + "CF_API_TOKEN": os.environ.get("CF_API_TOKEN", ""), + "CF_ACCOUNT_ID": os.environ.get("CF_ACCOUNT_ID", ""), + "CF_ZONE_ID": os.environ.get("CF_ZONE_ID", ""), + "CF_DOMAIN": os.environ.get("CF_DOMAIN", ""), +} +for key, value in cf_secrets.items(): + if value: + space_secrets[key] = value for key, value in space_secrets.items(): try: api.add_space_secret(space_id, key, value) @@ -181,7 +194,7 @@ for key, value in space_secrets.items(): # Variables (non-sensitive) variables = { - "MAX_MODEL_LEN": "4096", + "MAX_MODEL_LEN": "32768", "GPU_MEMORY_UTILIZATION": "0.70", "VLLM_GPU": "0", "TRAINER_GPU": "1", @@ -310,7 +323,7 @@ EOF # --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS EOF # Append deploy-time args to the trainer exec line (interpreter-agnostic). - sed -i "s|\(exec .*examples/mini_swe_env/train_swe_async_grpo.py\)|\1 --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS|" "$STAGE_DIR/start.sh" + sed -i "s|\(exec .*examples/mini_swe_env/train_swe_async_grpo.py\)|\1 --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS --num-generations $NUM_GENERATIONS|" "$STAGE_DIR/start.sh" FILE_COUNT=$(find "$STAGE_DIR" -type f | wc -l) echo " ✓ Staged $FILE_COUNT files" diff --git a/examples/mini_swe_env/deploy_collect_space.sh b/examples/mini_swe_env/deploy_collect_space.sh index 4cf241999..a2b7ea606 100755 --- a/examples/mini_swe_env/deploy_collect_space.sh +++ b/examples/mini_swe_env/deploy_collect_space.sh @@ -23,6 +23,10 @@ # --model MODEL_NAME Model name on vLLM endpoint (default: qwen-3.6-27b) # --api-url URL vLLM API base URL (default: https://api.siemens.com/llm/v1) # --api-key KEY vLLM API key (required, or set SWE_LLM_API_KEY) +# --cf-api-token TOKEN Cloudflare API token (or set CF_API_TOKEN) +# --cf-account-id ID Cloudflare account ID (or set CF_ACCOUNT_ID) +# --cf-zone-id ID Cloudflare zone ID (or set CF_ZONE_ID) +# --cf-domain DOMAIN Domain for sandbox tunnels (or set CF_DOMAIN) # --n-rollouts N Rollouts per task (default: 4) # --max-concurrent N Concurrent rollouts (default: 3) # --max-turns N Max agent turns (default: 50) @@ -45,6 +49,10 @@ SPACE_ID="${HF_SPACE_ID:-rycerzes/swe-teacher-collect}" MODEL="${SWE_LLM_MODEL:-qwen-3.6-27b}" API_URL="${SWE_LLM_BASE_URL:-https://api.siemens.com/llm/v1}" API_KEY="${SWE_LLM_API_KEY:-}" +CF_API_TOKEN="${CF_API_TOKEN:-}" +CF_ACCOUNT_ID="${CF_ACCOUNT_ID:-}" +CF_ZONE_ID="${CF_ZONE_ID:-}" +CF_DOMAIN="${CF_DOMAIN:-}" N_ROLLOUTS=4 MAX_CONCURRENT=3 MAX_TURNS=50 @@ -82,6 +90,10 @@ while [[ $# -gt 0 ]]; do --model) MODEL="$2"; shift 2 ;; --api-url) API_URL="$2"; shift 2 ;; --api-key) API_KEY="$2"; shift 2 ;; + --cf-api-token) CF_API_TOKEN="$2"; shift 2 ;; + --cf-account-id) CF_ACCOUNT_ID="$2"; shift 2 ;; + --cf-zone-id) CF_ZONE_ID="$2"; shift 2 ;; + --cf-domain) CF_DOMAIN="$2"; shift 2 ;; --n-rollouts) N_ROLLOUTS="$2"; shift 2 ;; --max-concurrent) MAX_CONCURRENT="$2"; shift 2 ;; --max-turns) MAX_TURNS="$2"; shift 2 ;; @@ -152,6 +164,11 @@ printf "║ %-54s ║\n" "Concurrent: $MAX_CONCURRENT" printf "║ %-54s ║\n" "Max Turns: $MAX_TURNS" printf "║ %-54s ║\n" "Max Tasks: ${MAX_TASKS:-all (230 Lite)}" printf "║ %-54s ║\n" "Rate Limit: $RATE_LIMIT req/min" +if [ -n "$CF_API_TOKEN" ]; then +printf "║ %-54s ║\n" "CF Tunnel: named (${CF_DOMAIN})" +else +printf "║ %-54s ║\n" "CF Tunnel: quick (trycloudflare.com)" +fi echo "╚══════════════════════════════════════════════════════════╝" echo "" @@ -175,6 +192,17 @@ space_secrets = { "SWE_LLM_API_KEY": "$API_KEY", "INTERCEPTION_AUTH_TOKEN": secrets.token_urlsafe(32), } + +# Cloudflare named tunnel secrets (optional — enables reliable sandbox connectivity) +cf_secrets = { + "CF_API_TOKEN": "$CF_API_TOKEN", + "CF_ACCOUNT_ID": "$CF_ACCOUNT_ID", + "CF_ZONE_ID": "$CF_ZONE_ID", + "CF_DOMAIN": "$CF_DOMAIN", +} +for key, value in cf_secrets.items(): + if value: + space_secrets[key] = value for key, value in space_secrets.items(): try: api.add_space_secret(space_id, key, value) @@ -297,7 +325,7 @@ RUN pip install --no-cache-dir -e ".[core]" && \ aiohttp \ datasets \ huggingface_hub \ - hf-sandbox \ + "hf-sandbox[named-tunnels] @ git+https://github.com/rycerzes/hf-sandbox@feat/named-tunnels" \ fastmcp # Copy remaining source From a1a764d47ae97683ace2292f51eb3d81d0fc9e14 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 02:10:41 +0530 Subject: [PATCH 70/79] fix: strip cf from the name --- .../async_grpo/space_app/deploy_hf_space.sh | 2 +- examples/mini_swe_env/deploy_collect_space.sh | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index c8634fc18..871e1435b 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -175,7 +175,7 @@ space_secrets = { "VLLM_API_KEY": "token", } -# Cloudflare named tunnel secrets (optional — enables reliable sandbox connectivity) +# named tunnel secrets (optional — enables reliable sandbox connectivity) cf_secrets = { "CF_API_TOKEN": os.environ.get("CF_API_TOKEN", ""), "CF_ACCOUNT_ID": os.environ.get("CF_ACCOUNT_ID", ""), diff --git a/examples/mini_swe_env/deploy_collect_space.sh b/examples/mini_swe_env/deploy_collect_space.sh index a2b7ea606..6b2edc979 100755 --- a/examples/mini_swe_env/deploy_collect_space.sh +++ b/examples/mini_swe_env/deploy_collect_space.sh @@ -23,9 +23,9 @@ # --model MODEL_NAME Model name on vLLM endpoint (default: qwen-3.6-27b) # --api-url URL vLLM API base URL (default: https://api.siemens.com/llm/v1) # --api-key KEY vLLM API key (required, or set SWE_LLM_API_KEY) -# --cf-api-token TOKEN Cloudflare API token (or set CF_API_TOKEN) -# --cf-account-id ID Cloudflare account ID (or set CF_ACCOUNT_ID) -# --cf-zone-id ID Cloudflare zone ID (or set CF_ZONE_ID) +# --cf-api-token TOKEN cf API token (or set CF_API_TOKEN) +# --cf-account-id ID cf account ID (or set CF_ACCOUNT_ID) +# --cf-zone-id ID cf zone ID (or set CF_ZONE_ID) # --cf-domain DOMAIN Domain for sandbox tunnels (or set CF_DOMAIN) # --n-rollouts N Rollouts per task (default: 4) # --max-concurrent N Concurrent rollouts (default: 3) @@ -167,7 +167,7 @@ printf "║ %-54s ║\n" "Rate Limit: $RATE_LIMIT req/min" if [ -n "$CF_API_TOKEN" ]; then printf "║ %-54s ║\n" "CF Tunnel: named (${CF_DOMAIN})" else -printf "║ %-54s ║\n" "CF Tunnel: quick (trycloudflare.com)" +printf "║ %-54s ║\n" "CF Tunnel: quick (auto-generated URL)" fi echo "╚══════════════════════════════════════════════════════════╝" echo "" @@ -193,7 +193,7 @@ space_secrets = { "INTERCEPTION_AUTH_TOKEN": secrets.token_urlsafe(32), } -# Cloudflare named tunnel secrets (optional — enables reliable sandbox connectivity) +# Named tunnel secrets (optional — enables reliable sandbox connectivity) cf_secrets = { "CF_API_TOKEN": "$CF_API_TOKEN", "CF_ACCOUNT_ID": "$CF_ACCOUNT_ID", From f4ab810e5f6fd1918d8c24c183ea84114af62989 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 03:32:44 +0530 Subject: [PATCH 71/79] fix: chat template prefix handling for multi-turn tool use - use trl prefix-preserving training template for reliable suffix computation - replace prev_prompt_ids with prev_base_ids (add_generation_prompt=false) - add fallback interstitial extraction on prefix mismatch - parse tool_call arguments from json strings to dicts for qwen3.5 - strip trailing eos tokens from vllm output --- .../mini_swe_env/async_grpo/rollout_worker.py | 206 +++++++++++++++++- .../async_grpo/space_app/Dockerfile | 4 +- 2 files changed, 197 insertions(+), 13 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index 521d70ea5..8e31eaf07 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -37,9 +37,16 @@ import requests try: - from trl.chat_template_utils import add_response_schema, parse_response + from trl.chat_template_utils import ( + add_response_schema, + get_training_chat_template, + is_chat_template_prefix_preserving, + parse_response, + ) except Exception: # pragma: no cover - defensive for older TRL versions add_response_schema = None + get_training_chat_template = None + is_chat_template_prefix_preserving = None parse_response = None try: @@ -147,6 +154,19 @@ def __init__( self._tokenizer = add_response_schema(self._tokenizer) except Exception as exc: _log.debug("add_response_schema unavailable for tokenizer: %s", exc) + + # Use TRL's prefix-preserving training template if available. + # The stock Qwen3.5 template is NOT prefix-preserving (it conditionally + # renders tags based on message position), which breaks our + # suffix computation. The training template fixes this. + self._chat_template: str | None = None + if get_training_chat_template is not None: + try: + self._chat_template = get_training_chat_template(self._tokenizer) + if self._chat_template: + _log.info("using TRL prefix-preserving training chat template") + except (ValueError, TypeError) as exc: + _log.debug("get_training_chat_template failed: %s", exc) self._vllm_base_url = vllm_base_url.rstrip("/") self._vllm_api_key = vllm_api_key self._vllm_model = vllm_model @@ -538,7 +558,9 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None all_lps: list[float] = [] initial_prompt_ids: list[int] | None = None - prev_prompt_ids: list[int] | None = None + # prev_base_ids: render of all messages so far WITHOUT add_generation_prompt. + # The training template guarantees this is prefix-preserving across turns. + prev_base_ids: list[int] | None = None turns = 0 answer_called = False @@ -570,13 +592,45 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None all_ids.extend(current_prompt_ids) all_mask.extend([0] * len(current_prompt_ids)) all_lps.extend([0.0] * len(current_prompt_ids)) - elif prev_prompt_ids is not None: - # Subsequent turns: the delta between prev generation end - # and this turn's prompt is the tool-result suffix. - # prev_prompt_ids + prev_turn_ids = end of last generation - # current_prompt_ids = prev_prompt_ids + prev_turn_ids + suffix_ids - prev_len = len(prev_prompt_ids) - suffix_ids = current_prompt_ids[prev_len:] + elif prev_base_ids is not None: + # Subsequent turns: compute the interstitial (tool-result + # suffix) between the last generation and this turn's prompt. + # + # We use Polar's approach (arXiv:2605.24220 §3.4.2): + # The "base" render (add_generation_prompt=False) is + # prefix-preserving across turns — guaranteed by TRL's + # training chat template. The suffix = tokens in the + # current prompt that come AFTER the previous base. + prev_len = len(prev_base_ids) + + # Prefix validation. + if current_prompt_ids[:prev_len] != prev_base_ids: + _log.warning( + "prefix mismatch at turn %d (prev_base=%d tokens, " + "first diff at idx %d). Using EOT fallback.", + turns, + prev_len, + next( + ( + i + for i, (a, b) in enumerate( + zip(current_prompt_ids, prev_base_ids) + ) + if a != b + ), + -1, + ), + ) + # Fallback: use Polar's EOT-based interstitial detection + # on the canonical tail from the full prompt. + suffix_ids = _slice_interstitial_fallback( + current_prompt_ids, + prev_base_ids, + self._tokenizer.eos_token_id, + ) + else: + suffix_ids = current_prompt_ids[prev_len:] + all_ids.extend(suffix_ids) all_mask.extend([0] * len(suffix_ids)) all_lps.extend([0.0] * len(suffix_ids)) @@ -592,8 +646,16 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None all_mask.extend([1] * len(turn_ids)) all_lps.extend(turn_lps) - # For next turn's suffix computation: - prev_prompt_ids = current_prompt_ids + turn_ids + # For next turn's suffix computation: compute the "base" render + # of all messages so far (WITHOUT add_generation_prompt). This + # is prefix-preserving across turns — the next turn's prompt + # (rendered with add_gen=True) will start with these tokens. + # + # This approach avoids retokenization drift at the + # add_generation_prompt boundary (e.g. \n vs + # \n\n\n\n) which would cause prefix mismatches + # with the naive prev_prompt = prompt + turn_ids approach. + prev_base_ids = self._render_base_ids(messages, tools) # ── Build chat response for Pi ──────────────────── assistant_message = _parse_assistant_message( @@ -694,6 +756,48 @@ def _render_prompt_ids( # ``tools=[]`` can trigger unwanted boilerplate in some templates. if tools: kwargs["tools"] = tools + # Use TRL's prefix-preserving training template when available. + if self._chat_template: + kwargs["chat_template"] = self._chat_template + + # Qwen3.5's chat template iterates tool_call.arguments with |items, + # expecting a dict. The OpenAI API spec (and our _normalize_tool_calls) + # stores arguments as a JSON *string*. Parse them into dicts here so + # the Jinja template can iterate key-value pairs. + messages = _ensure_tool_call_arguments_parsed(messages) + + try: + ids = self._tokenizer.apply_chat_template(messages, **kwargs) + except TypeError: + kwargs.pop("tools", None) + ids = self._tokenizer.apply_chat_template(messages, **kwargs) + return cast(list[int], ids) + + def _render_base_ids( + self, + messages: list[dict[str, Any]], + tools: list[dict[str, Any]] | None, + ) -> list[int]: + """Render messages WITHOUT add_generation_prompt. + + The resulting token sequence is prefix-preserving: appending tool + results to the messages and re-rendering (with add_gen=True) will + produce a token sequence whose first ``len(base_ids)`` tokens match + exactly. This property is what makes suffix computation reliable + across turns (see TITO blog, Polar §3.4.2, TRL's + ``is_chat_template_prefix_preserving``). + """ + kwargs: dict[str, Any] = { + "add_generation_prompt": False, + "return_dict": False, + } + if tools: + kwargs["tools"] = tools + if self._chat_template: + kwargs["chat_template"] = self._chat_template + + messages = _ensure_tool_call_arguments_parsed(messages) + try: ids = self._tokenizer.apply_chat_template(messages, **kwargs) except TypeError: @@ -742,6 +846,49 @@ def _generate( # ── Helpers ──────────────────────────────────────────────────────────── +def _slice_interstitial_fallback( + current_prompt_ids: list[int], + prev_base_ids: list[int], + eos_token_id: int | None, +) -> list[int]: + """Fallback interstitial extraction when prefix check fails. + + Uses Polar's EOT-based detection: find the longest common prefix between + ``current_prompt_ids`` and ``prev_base_ids``, then return everything in + ``current_prompt_ids`` after that common prefix. This handles edge cases + where the template isn't perfectly prefix-preserving (e.g. thinking-token + variations). + """ + # Find longest common prefix + common_len = 0 + for a, b in zip(current_prompt_ids, prev_base_ids): + if a != b: + break + common_len += 1 + + # Return everything after the common prefix in current_prompt_ids + return current_prompt_ids[common_len:] + + +def _strip_trailing_eos(ids: list[int], eos_token_id: int | None) -> list[int]: + """Strip trailing EOS token from generated token IDs. + + vLLM includes the stop token (e.g. ``<|im_end|>``) in the returned + ``token_ids`` when ``finish_reason == 'stop'``. The chat template will + re-render that same token as part of the canonical assistant block + (``{{- '<|im_end|>\n' }}``). To avoid double-counting it in the prefix + computation, we strip it here. + + This mirrors TRL's ``_get_tool_suffix_ids`` EOS-trimming logic and Polar's + interstitial boundary detection (Section 3.4.2 of arXiv:2605.24220). + """ + if eos_token_id is None or not ids: + return ids + if ids[-1] == eos_token_id: + return ids[:-1] + return ids + + def _get_messages(intercept: dict[str, Any]) -> list[dict[str, Any]]: msgs = intercept.get("messages") if isinstance(msgs, list) and msgs: @@ -764,6 +911,43 @@ def _get_tools(intercept: dict[str, Any]) -> list[dict[str, Any]] | None: return None +def _ensure_tool_call_arguments_parsed( + messages: list[dict[str, Any]], +) -> list[dict[str, Any]]: + """Deep-copy messages, parsing tool_call arguments from JSON strings to dicts. + + Qwen3.5's chat template iterates ``tool_call.arguments|items`` expecting a + mapping. The OpenAI API (and our interception layer) stores arguments as a + JSON string. This bridges the gap. + """ + out: list[dict[str, Any]] = [] + for msg in messages: + tool_calls = msg.get("tool_calls") + if not tool_calls or not isinstance(tool_calls, list): + out.append(msg) + continue + new_tcs: list[dict[str, Any]] = [] + for tc in tool_calls: + if not isinstance(tc, dict): + new_tcs.append(tc) + continue + fn = tc.get("function") + if not isinstance(fn, dict): + new_tcs.append(tc) + continue + args = fn.get("arguments") + if isinstance(args, str): + try: + args = json.loads(args) + except (json.JSONDecodeError, TypeError): + args = {} + new_tcs.append( + {**tc, "function": {**fn, "arguments": args if isinstance(args, dict) else {}}} + ) + out.append({**msg, "tool_calls": new_tcs}) + return out + + def _normalize_tool_calls(raw_tool_calls: Any) -> list[dict[str, Any]]: normalized: list[dict[str, Any]] = [] if not isinstance(raw_tool_calls, list): diff --git a/examples/mini_swe_env/async_grpo/space_app/Dockerfile b/examples/mini_swe_env/async_grpo/space_app/Dockerfile index e800b3680..de03cd1ca 100644 --- a/examples/mini_swe_env/async_grpo/space_app/Dockerfile +++ b/examples/mini_swe_env/async_grpo/space_app/Dockerfile @@ -6,7 +6,7 @@ FROM vllm/vllm-openai:v0.21.0 # System deps + Docker CLI (for Docker sandbox backend) RUN apt-get update -qq && \ - apt-get install -y -qq --no-install-recommends curl ca-certificates docker.io && \ + apt-get install -y -qq --no-install-recommends curl ca-certificates docker.io git && \ update-ca-certificates && \ rm -rf /var/lib/apt/lists/* @@ -37,7 +37,7 @@ RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ -e "." \ -e "envs/mini_swe_env" && \ uv pip install --system --no-cache \ - "trl>=1.6.0" \ + "trl>=1.5.1" \ "accelerate>=1.13.0" \ "hf-sandbox[named-tunnels] @ git+https://github.com/rycerzes/hf-sandbox@feat/named-tunnels" \ "datasets>=4.8.0" \ From 6c9e121aa4c0a7cdf1c60dd54e6869c915619187 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 03:39:17 +0530 Subject: [PATCH 72/79] chore: clarify RolloutSample docstring regarding TRL's reward functions --- examples/mini_swe_env/async_grpo/rollout_worker.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index 8e31eaf07..d2942df11 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -92,7 +92,12 @@ def _vllm_version() -> tuple[int, ...]: @dataclass class RolloutSample: - """Matches the fields TRL's ``RolloutQueueDataset`` reads.""" + """Matches the fields TRL's ``RolloutQueueDataset`` reads. + + Note: TRL's own RolloutSample also has ``prompt`` and ``completion`` + (message-level) but those are only used by TRL's built-in reward_funcs. + We compute rewards via ``session.verify()`` so they're unnecessary. + """ input_ids: list[int] completion_mask: list[int] From 74acb442e3fca4b60498c4bc4b7336a830f5c72a Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 14:30:04 +0530 Subject: [PATCH 73/79] refactor: ctx overflow handling and model length config in rollout worker --- .../mini_swe_env/async_grpo/rollout_worker.py | 204 ++++++++++++++++-- .../async_grpo/space_app/deploy_hf_space.sh | 4 +- .../async_grpo/space_app/start.sh | 2 +- examples/mini_swe_env/train_swe_async_grpo.py | 2 +- src/openenv/core/harness/agents/cli_driver.py | 17 ++ .../harness/agents/interception_server.py | 35 +++ 6 files changed, 245 insertions(+), 19 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index d2942df11..441c2b9e6 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -128,6 +128,15 @@ class WorkerConfig: post_response_grace_s: float = 10.0 stop_on_idle_terminal_response: bool = True idle_backoff_s: float = 0.5 + # vLLM max_model_len for pre-generation guard. If the prompt + + # max_completion_tokens exceeds this, the rollout terminates early + # with reward=0 rather than crashing the engine. Set to match + # vLLM's --max-model-len. + max_model_len: int = 40960 + # Number of context overflow failures before a task is blacklisted + # for the remainder of the training run. Prevents infinite retry + # loops on tasks whose prompts inherently exceed the context window. + max_context_overflow_per_task: int = 3 # ── Worker ───────────────────────────────────────────────────────────── @@ -199,6 +208,13 @@ def __init__( # task_id → list of (RolloutSample with raw reward in .advantage) self._pending_groups: dict[str, list[RolloutSample]] = {} + # ── Context overflow tracking ──────────────────────────── + # Track tasks that repeatedly exceed context to avoid infinite + # retry loops. Failed sessions get reward=0 and are excluded + # from group normalization in _score_and_push_group. + self._overflow_counts: dict[str, int] = {} + self._overflow_lock = threading.Lock() + # Prefix to prepend to trainer parameter names so they match vLLM's # model architecture. For VLM models like Qwen3_5ForConditionalGeneration # served with --language-model-only, vLLM parameters are at @@ -457,21 +473,69 @@ def _model_ver(self) -> int: with self._lock: return self._model_version + def _is_task_blacklisted(self, task_id: str) -> bool: + """Check if a task has been blacklisted due to repeated context overflow.""" + with self._overflow_lock: + return ( + self._overflow_counts.get(task_id, 0) + >= self._cfg.max_context_overflow_per_task + ) + + def _record_context_overflow(self, task_id: str) -> None: + """Record a context overflow for a task. After max attempts, + abandon the group with reward=0 for all pending samples.""" + abandon_group = False + with self._overflow_lock: + self._overflow_counts[task_id] = ( + self._overflow_counts.get(task_id, 0) + 1 + ) + count = self._overflow_counts[task_id] + if count >= self._cfg.max_context_overflow_per_task: + _log.warning( + "task %s blacklisted after %d context overflows — " + "abandoning group with reward=0", + task_id, + count, + ) + abandon_group = True + + if abandon_group: + # Flush the partial group: failed sessions get reward=0 + # and are excluded from group normalization. + with self._group_lock: + partial = self._pending_groups.pop(task_id, None) + if partial: + for sample in partial: + sample.advantage = 0.0 + sample.metrics["context_overflow_abandoned"] = 1.0 + try: + self.rollout_buffer.put(sample, timeout=1.0) + except queue.Full: + pass + def _next_group_task(self) -> SWETask: """Pick a task that needs more rollouts to complete its group. Prioritizes tasks that already have partial groups (some rollouts done but < num_generations). Falls back to a fresh task. + Skips tasks blacklisted due to repeated context overflow. """ with self._group_lock: # Find a task with a partial group that needs more rollouts for task_id, samples in self._pending_groups.items(): if len(samples) < self._cfg.num_generations: + if self._is_task_blacklisted(task_id): + continue # Return the corresponding task for t in self._tasks: if t.instance_id == task_id: return t - # No partial groups — start a fresh task + # No partial groups — start a fresh task (skip blacklisted) + for _ in range(len(self._tasks)): + task = self._next_task() + if not self._is_task_blacklisted(task.instance_id): + return task + # All tasks blacklisted — fatal (shouldn't happen with 293 tasks) return self._next_task() def _submit_to_group(self, task: SWETask, sample: RolloutSample) -> None: @@ -495,21 +559,51 @@ def _submit_to_group(self, task: SWETask, sample: RolloutSample) -> None: def _score_and_push_group( self, task_id: str, group: list[RolloutSample] ) -> None: - """Compute group-relative advantages (GRPO) and push to buffer.""" + """Compute group-relative advantages (GRPO) and push to buffer. + + Failed sessions (context_overflow) are excluded from the group + mean/std and get advantage=0, matching Polar's reward_post_process + which zeros FAILED/ABORTED trajectories. + """ rewards = [s.advantage for s in group] # raw rewards stored here n = len(rewards) - mean_r = sum(rewards) / n - var_r = sum((r - mean_r) ** 2 for r in rewards) / n + + # Exclude failed sessions from the baseline computation. + valid_mask = [ + s.metrics.get("context_overflow", 0.0) == 0.0 for s in group + ] + valid_rewards = [r for r, v in zip(rewards, valid_mask) if v] + + if not valid_rewards: + # All sessions failed — no signal. + for sample in group: + sample.advantage = 0.0 + sample.metrics["group_reward_mean"] = 0.0 + sample.metrics["group_reward_std"] = 0.0 + sample.metrics["group_size"] = float(n) + try: + self.rollout_buffer.put(sample, timeout=2.0) + except queue.Full: + _log.warning("queue full, dropping sample from group %s", task_id) + _log.info( + "group complete (all failed): task=%s n=%d", task_id, n + ) + return + + mean_r = sum(valid_rewards) / len(valid_rewards) + var_r = sum((r - mean_r) ** 2 for r in valid_rewards) / len(valid_rewards) std_r = var_r**0.5 for i, sample in enumerate(group): - if std_r > 1e-8: + if not valid_mask[i]: + # Failed session: zero advantage, zero gradient. + sample.advantage = 0.0 + elif std_r > 1e-8: sample.advantage = (rewards[i] - mean_r) / std_r else: - # All rewards identical (e.g. all 0 or all 1) — zero advantage + # All valid rewards identical — zero advantage sample.advantage = 0.0 - # Add group metrics sample.metrics["group_reward_mean"] = mean_r sample.metrics["group_reward_std"] = std_r sample.metrics["group_size"] = float(n) @@ -522,8 +616,8 @@ def _score_and_push_group( ) _log.info( - "group complete: task=%s n=%d reward_mean=%.3f reward_std=%.3f", - task_id, n, mean_r, std_r, + "group complete: task=%s n=%d valid=%d reward_mean=%.3f reward_std=%.3f", + task_id, n, len(valid_rewards), mean_r, std_r, ) def _loop(self, idx: int) -> None: @@ -537,6 +631,34 @@ def _loop(self, idx: int) -> None: eid = f"swe-{idx}-{uuid.uuid4().hex[:8]}" try: sample = asyncio.run(self._rollout(task, eid)) + except SWERolloutWorker.ContextOverflowError as exc: + # Record the overflow and submit a zero-reward sample so + # the group can still progress. After max_context_overflow_per_task + # failures the task is blacklisted. + _log.warning( + "context overflow worker=%d task=%s: %s", + idx, + task.instance_id, + exc, + ) + self._record_context_overflow(task.instance_id) + pad = getattr(self._tokenizer, "pad_token_id", 0) or 0 + overflow_sample = RolloutSample( + input_ids=[pad], + completion_mask=[1], + old_log_probs=[0.0], + advantage=0.0, + model_version=self._model_ver(), + metrics={ + "reward": 0.0, + "turns": 0.0, + "context_overflow": 1.0, + "answer_called": 0.0, + }, + ) + self._submit_to_group(task, overflow_sample) + time.sleep(self._cfg.idle_backoff_s) + continue except Exception: _log.exception("rollout failed worker=%d id=%s", idx, task.instance_id) time.sleep(self._cfg.idle_backoff_s) @@ -643,9 +765,27 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None turns += 1 # ── Generate via /v1/completions ────────────────── - turn_ids, turn_lps, text, finish_reason = self._generate( - current_prompt_ids - ) + # If context overflows on turn 1, let it propagate to _loop() + # which emits a zero-reward placeholder. On later turns, + # terminate the rollout with the partial trajectory (reward=0) + # so the session still contributes to the group. + try: + turn_ids, turn_lps, text, finish_reason = self._generate( + current_prompt_ids + ) + except SWERolloutWorker.ContextOverflowError: + if turns <= 1: + raise + _log.warning( + "context overflow at turn %d for task=%s — " + "terminating with partial trajectory (%d tokens)", + turns, + task.instance_id, + len(all_ids), + ) + rollout_stop_reason = "context_overflow" + self._record_context_overflow(task.instance_id) + break all_ids.extend(turn_ids) all_mask.extend([1] * len(turn_ids)) @@ -714,8 +854,12 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None pending_intercept = maybe_next # ── Reward ──────────────────────────────────────────── - vr = session.verify(transcript=[]) - reward = float(getattr(vr, "env_reward", 0.0) or 0.0) + # Context overflow means the agent didn't finish; assign reward=0. + if rollout_stop_reason == "context_overflow": + reward = 0.0 + else: + vr = session.verify(transcript=[]) + reward = float(getattr(vr, "env_reward", 0.0) or 0.0) if not all_lps: pad = getattr(self._tokenizer, "pad_token_id", 0) or 0 @@ -733,6 +877,9 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None "reward": reward, "turns": float(turns), "answer_called": float(answer_called), + "context_overflow": float( + rollout_stop_reason == "context_overflow" + ), "terminal_idle_stop": float( rollout_stop_reason in { @@ -810,6 +957,13 @@ def _render_base_ids( ids = self._tokenizer.apply_chat_template(messages, **kwargs) return cast(list[int], ids) + class ContextOverflowError(RuntimeError): + """Raised when prompt + max_tokens exceeds max_model_len. + + Caught in _loop() to terminate the session gracefully with + reward=0, avoiding the infinite retry loop. + """ + def _generate( self, prompt_ids: list[int], @@ -817,7 +971,21 @@ def _generate( """POST /v1/completions with token IDs. Returns: ``(token_ids, token_logprobs, text, finish_reason)``. + + Raises ContextOverflowError if the prompt would exceed + max_model_len. This is caught in _loop() for graceful + termination with reward=0. """ + # Pre-generation guard: check before sending to vLLM so we don't + # waste a round trip and risk the engine going idle. + total_needed = len(prompt_ids) + self._cfg.max_completion_tokens + if total_needed > self._cfg.max_model_len: + raise SWERolloutWorker.ContextOverflowError( + f"prompt ({len(prompt_ids)}) + max_tokens " + f"({self._cfg.max_completion_tokens}) = {total_needed} > " + f"max_model_len ({self._cfg.max_model_len})" + ) + body = { "model": self._vllm_model, "prompt": prompt_ids, @@ -837,7 +1005,13 @@ def _generate( timeout=self._cfg.request_timeout_s, ) if resp.status_code != 200: - raise RuntimeError(f"vllm {resp.status_code}: {resp.text[:400]}") + error_text = resp.text[:400] + # Detect context overflow from vLLM's 400 response + if resp.status_code == 400 and "max_model_len" in error_text: + raise SWERolloutWorker.ContextOverflowError( + f"vLLM rejected: {error_text}" + ) + raise RuntimeError(f"vllm {resp.status_code}: {error_text}") choice = resp.json()["choices"][0] return ( diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index 871e1435b..591e7df08 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -194,8 +194,8 @@ for key, value in space_secrets.items(): # Variables (non-sensitive) variables = { - "MAX_MODEL_LEN": "32768", - "GPU_MEMORY_UTILIZATION": "0.70", + "MAX_MODEL_LEN": "40960", + "GPU_MEMORY_UTILIZATION": "0.75", "VLLM_GPU": "0", "TRAINER_GPU": "1", "INTERCEPTION_HOST": "0.0.0.0", diff --git a/examples/mini_swe_env/async_grpo/space_app/start.sh b/examples/mini_swe_env/async_grpo/space_app/start.sh index 696d155f0..75feb2e17 100755 --- a/examples/mini_swe_env/async_grpo/space_app/start.sh +++ b/examples/mini_swe_env/async_grpo/space_app/start.sh @@ -14,7 +14,7 @@ set -e MODEL="${SWE_MODEL:?ERROR: Set SWE_MODEL (e.g. Qwen/Qwen3-1.7B)}" VLLM_PORT="${VLLM_PORT:-8000}" VLLM_KEY="${VLLM_API_KEY:-token}" -MAX_MODEL_LEN="${MAX_MODEL_LEN:-4096}" +MAX_MODEL_LEN="${MAX_MODEL_LEN:-49152}" GPU_MEM_UTIL="${GPU_MEMORY_UTILIZATION:-0.9}" # Prefer repo-local venv Python for local/dev runs; fall back to image python. diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 1df553234..033cd897b 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -14,7 +14,7 @@ Prerequisites: CUDA_VISIBLE_DEVICES=0 vllm serve Qwen/Qwen3-1.7B \\ - --tensor-parallel-size 1 --max-model-len 4096 + --tensor-parallel-size 1 --max-model-len 40960 CUDA_VISIBLE_DEVICES=1 \\ SWE_MODEL=Qwen/Qwen3-1.7B \\ diff --git a/src/openenv/core/harness/agents/cli_driver.py b/src/openenv/core/harness/agents/cli_driver.py index ffab1a090..aedaec555 100644 --- a/src/openenv/core/harness/agents/cli_driver.py +++ b/src/openenv/core/harness/agents/cli_driver.py @@ -210,6 +210,9 @@ async def next_request( self._interception_queue.get, timeout=min(remaining, 1.0), ) + # None sentinel = agent process exited (sent by /exit endpoint) + if request_id is None: + return None intercept = server.get_intercept(request_id) if intercept is not None: return intercept @@ -507,6 +510,20 @@ def _start_agent( if self.mode == "interception_gate" and self._interception_server is not None: envs["OPENAI_API_KEY"] = self._interception_server.secret envs["ANTHROPIC_API_KEY"] = self._interception_server.secret + + # Append an exit notification so the InterceptionServer detects + # agent exit immediately instead of waiting for the full timeout. + # The /exit endpoint enqueues a None sentinel on the request queue, + # causing next_request() to return None. + if base_url_override: + exit_url = f"{base_url_override.rstrip('/')}/exit" + api_key = self._interception_server.secret + cmd = ( + f"{{ {cmd} ; }} ; " + f'curl -sf -X POST -H "Authorization: Bearer {api_key}" ' + f'"{exit_url}" || true' + ) + return sandbox.start_bg(cmd, envs=envs) def _write_pi_models_config( diff --git a/src/openenv/core/harness/agents/interception_server.py b/src/openenv/core/harness/agents/interception_server.py index fa735f0c0..3e62c90e9 100644 --- a/src/openenv/core/harness/agents/interception_server.py +++ b/src/openenv/core/harness/agents/interception_server.py @@ -126,6 +126,14 @@ async def start(self) -> None: "/rollout/{rollout_id}/v1/tools/{tool_name}", self._handle_tool_call, ) + app.router.add_post( + "/rollout/{rollout_id}/exit", + self._handle_exit, + ) + app.router.add_post( + "/rollout/{rollout_id}/v1/exit", + self._handle_exit, + ) app.router.add_get("/health", self._handle_health) runner = web.AppRunner(app) await runner.setup() @@ -363,6 +371,33 @@ def _authorized(self, request: web.Request) -> bool: async def _handle_health(self, request: web.Request) -> web.Response: return web.json_response({"status": "ok", **self.stats()}) + async def _handle_exit(self, request: web.Request) -> web.Response: + """Handle agent process exit notification. + + Called by the sandbox entrypoint after the agent process exits. + Enqueues a sentinel ``None`` on the rollout's request queue so that + ``next_request()`` returns immediately instead of waiting for the + full timeout. + """ + rollout_id = request.match_info["rollout_id"] + with self._state_lock: + rollout = self.active_rollouts.get(rollout_id) + if rollout is None: + return web.json_response({"status": "ignored", "reason": "unknown rollout_id"}) + + queue = rollout.get("request_id_queue") + if queue is not None: + try: + queue.put_nowait(None) # sentinel: signals "agent exited" + except Exception: + pass + + _log.info( + "interception_exit_signal rollout_id=%s", + rollout_id, + ) + return web.json_response({"status": "ok"}) + async def _handle_tool_call(self, request: web.Request) -> web.Response: if not self._authorized(request): return web.json_response({"error": "Unauthorized"}, status=401) From 2a90f471f7135f0eb01434a58c5c9dfc4da1518c Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 16:05:51 +0530 Subject: [PATCH 74/79] fix: grading, context management and message truncation Co-authored-by: Ben Burtenshaw --- envs/mini_swe_env/grading.py | 40 +++- envs/mini_swe_env/harness.py | 196 +++++++++++++-- .../mini_swe_env/async_grpo/rollout_worker.py | 226 +++++++++++++++++- 3 files changed, 427 insertions(+), 35 deletions(-) diff --git a/envs/mini_swe_env/grading.py b/envs/mini_swe_env/grading.py index d09f46a4c..d0c6c120d 100644 --- a/envs/mini_swe_env/grading.py +++ b/envs/mini_swe_env/grading.py @@ -4,16 +4,16 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -"""Binary grading for SWE-Gym tasks. +"""Grading helpers for SWE-Gym tasks. -This module is intentionally SWE-Gym-native and does not depend on -external repo/version parser maps. +Supports both the canonical binary SWE-Gym reward and an optional +case-fraction shaping mode for RL experiments: -The grading contract matches SWE-Gym semantics: - -- reward = 1.0 iff every FAIL_TO_PASS test passes AND every PASS_TO_PASS - test still passes. -- reward = 0.0 otherwise. +- ``binary``: reward = 1.0 iff every FAIL_TO_PASS test passes AND every + PASS_TO_PASS test still passes, else 0.0. +- ``case_fraction``: reward = passed_cases / total_cases, while + ``resolved`` still requires every FAIL_TO_PASS and PASS_TO_PASS case to + pass. """ from __future__ import annotations @@ -32,6 +32,7 @@ class GradeResult: __slots__ = ( "reward", + "case_fraction", "resolved", "patch_applied", "tests_status", @@ -42,12 +43,14 @@ def __init__( self, *, reward: float, + case_fraction: float, resolved: bool, patch_applied: bool, tests_status: dict[str, Any] | None = None, instance_id: str = "", ): self.reward = reward + self.case_fraction = case_fraction self.resolved = resolved self.patch_applied = patch_applied self.tests_status = tests_status @@ -55,7 +58,8 @@ def __init__( def __repr__(self) -> str: return ( - f"GradeResult(reward={self.reward}, resolved={self.resolved}, " + f"GradeResult(reward={self.reward}, case_fraction={self.case_fraction}, " + f"resolved={self.resolved}, " f"patch_applied={self.patch_applied}, instance_id={self.instance_id!r})" ) @@ -65,6 +69,7 @@ def grade_from_case_results( case_results: dict[str, bool], *, patch_applied: bool = True, + reward_mode: str = "binary", ) -> GradeResult: """Grade directly from per-test-case outcomes. @@ -72,14 +77,24 @@ def grade_from_case_results( task: SWE-Gym task with FAIL_TO_PASS and PASS_TO_PASS lists. case_results: Mapping ``test_case -> passed``. patch_applied: Whether test patch was successfully applied. + reward_mode: ``"binary"`` or ``"case_fraction"``. """ if not isinstance(case_results, dict): raise GradingError("case_results must be a dict[str, bool]") + if reward_mode not in {"binary", "case_fraction"}: + raise GradingError( + f"Unknown reward_mode {reward_mode!r}; expected 'binary' or 'case_fraction'" + ) def _passed(case: str) -> bool: return bool(case_results.get(case, False)) + all_cases = list(dict.fromkeys([*task.FAIL_TO_PASS, *task.PASS_TO_PASS])) + passed_cases = sum(1 for case in all_cases if _passed(case)) + total_cases = len(all_cases) + case_fraction = (passed_cases / total_cases) if total_cases else 0.0 + resolved = all(_passed(case) for case in task.FAIL_TO_PASS) and all( _passed(case) for case in task.PASS_TO_PASS ) @@ -95,8 +110,13 @@ def _passed(case: str) -> bool: }, } + reward = 1.0 if resolved else 0.0 + if reward_mode == "case_fraction": + reward = case_fraction + return GradeResult( - reward=1.0 if resolved else 0.0, + reward=reward, + case_fraction=case_fraction, resolved=resolved, patch_applied=patch_applied, tests_status=tests_status, diff --git a/envs/mini_swe_env/harness.py b/envs/mini_swe_env/harness.py index f04911fff..595a521d4 100644 --- a/envs/mini_swe_env/harness.py +++ b/envs/mini_swe_env/harness.py @@ -48,13 +48,14 @@ import asyncio import json +import os import queue as _queue_mod import logging import shlex import time import uuid from dataclasses import dataclass, field -from typing import Any, Literal +from typing import Any, Callable, Literal from openenv.core.harness import Message, ResourceSessionFactory, VerifyResult from openenv.core.harness.agents import get_agent_spec @@ -79,6 +80,33 @@ VERIFY_TIMEOUT_S = 300 SETUP_TIMEOUT_S = 600 +DEFAULT_GIT_CHECKOUT_TIMEOUT_S = 300 + + +def _git_checkout_timeout_s() -> int: + """Read configurable git checkout timeout from environment.""" + for name in ("SWE_GIT_CHECKOUT_TIMEOUT_S", "SWE_LOCAL_GIT_CHECKOUT_TIMEOUT_S"): + value = os.environ.get(name) + if value is None: + continue + try: + timeout_s = int(value) + except ValueError: + _log.warning( + "%s must be an integer; using %ss", + name, + DEFAULT_GIT_CHECKOUT_TIMEOUT_S, + ) + return DEFAULT_GIT_CHECKOUT_TIMEOUT_S + if timeout_s > 0: + return timeout_s + _log.warning( + "%s must be positive; using %ss", + name, + DEFAULT_GIT_CHECKOUT_TIMEOUT_S, + ) + return DEFAULT_GIT_CHECKOUT_TIMEOUT_S + return DEFAULT_GIT_CHECKOUT_TIMEOUT_S _ANSWER_TOOL_DEFINITION = { "type": "function", @@ -101,43 +129,103 @@ {problem_statement} +{hints_block} + # Task Instructions ## Overview -You're a software engineer working on a codebase at /testbed. +You're a software engineer working on a codebase at {workdir}. Your task is to fix the issue described in the PR description above by making changes to the source code (non-test files). ## Important Boundaries -- MODIFY: Regular source code files in /testbed +- MODIFY: Regular source code files in {workdir} - DO NOT MODIFY: Tests, configuration files (pyproject.toml, setup.cfg, etc.) +- Test edits do not help. Grading may restore evaluation tests before running. +- Plain text does not execute anything. To inspect files, run commands, edit + code, or submit a fix, you must use your available tools. +- The repo environment is already bootstrapped. Do not create a new virtualenv + or reinstall the project unless a command clearly shows it is necessary. +- Do not use `git commit`, `git branch`, or `git push`. Grading only checks the + final working tree state. +- Each bash tool call runs in a fresh shell. If commands depend on shell state, + combine them into one bash invocation; `source` and shell variables do not + persist across separate tool calls. +- Do not claim a file was changed or a test passed unless you actually used a + tool and observed that result. ## Recommended Workflow -1. Analyze the codebase by finding and reading relevant files -2. Create a script to reproduce the issue -3. Edit the source code to resolve the issue -4. Verify your fix works by running your script again -5. Test edge cases to ensure your fix is robust - -## Submitting Your Answer -When you've completed your work and verified your fix, call the `answer` -tool to submit your solution for grading. This runs the test suite and -returns whether the issue is resolved. - -You cannot continue working after submitting — make sure your fix is -tested before calling `answer`. +1. Start with the maintainer hints or issue description. If they point to a + likely source file or function, inspect that before broad repo-wide searches. + If the issue mentions an identifier or exact error text, grep for that first. +2. Reproduce the issue with focused commands instead of exhaustive greps over + unrelated tests. +3. Edit the source code to resolve the issue. +4. Verify the fix with targeted commands, then check for obvious regressions. +5. If the `answer` tool is available, call it only after you have actually + changed source code and verified the result. + +{submission_block} """ -def _wrap_instruction(problem_statement: str) -> str: +def _bool_env(name: str, default: bool) -> bool: + raw = os.environ.get(name) + if raw is None: + return default + return raw.strip().lower() in {"1", "true", "yes", "on"} + + +def _answer_tool_enabled() -> bool: + return _bool_env("SWE_ENABLE_ANSWER_TOOL", True) + + +def _wrap_instruction( + problem_statement: str, + *, + hints_text: str = "", + workdir: str = TESTBED, + answer_tool_enabled: bool = True, +) -> str: """Wrap a problem statement with SWE-Gym-style task instructions. Tells the agent about the workflow, boundaries, and crucially about the ``answer`` tool for submission. """ + hints = (hints_text or "").strip() + hints_block = "" + if hints: + hints_block = ( + "\n" + "Additional context from issue triage or maintainers:\n" + f"{hints}\n" + "" + ) + if answer_tool_enabled: + submission_block = ( + "## Submitting Your Answer\n" + "When you've completed your work and verified your fix, call the " + "`answer`\n" + "tool to submit your solution for grading. This runs the test suite and\n" + "returns whether the issue is resolved.\n\n" + "You cannot continue working after submitting — make sure your fix is\n" + "tested before calling `answer`." + ) + else: + submission_block = ( + "## Ending The Run\n" + "There is no `answer` tool in this run.\n" + "Keep working until you have made and checked the best source-code " + "fix you can.\n" + "Your final repo state will be graded automatically when the session " + "ends." + ) return _SWE_INSTRUCTION_TEMPLATE.format( problem_statement=problem_statement, + hints_block=hints_block, + workdir=workdir, + submission_block=submission_block, ) @@ -200,7 +288,7 @@ def __init__( self._answer_called = False self._answer_bridged = False # Callback for post-hoc grading: (sandbox, swe_task) -> (reward, resolved) - self._grade_fn = grade_fn + self._grade_fn: Callable[..., tuple[float, bool]] | None = grade_fn @property def swe_task(self) -> SWETask: @@ -565,7 +653,7 @@ def create( interception_queue=interception_queue, ) - if self._mode == "interception_gate": + if self._mode == "interception_gate" and _answer_tool_enabled(): self._register_answer_tool(session) return session @@ -587,7 +675,7 @@ def _prepare_repo(self, sandbox: SandboxHandle, task: SWETask) -> None: r = sandbox.exec( f"git checkout --quiet {task.base_commit}", cwd=TESTBED, - timeout=60, + timeout=_git_checkout_timeout_s(), ) if r.exit_code != 0: raise RuntimeError( @@ -610,8 +698,14 @@ def _build_agent_task(self, swe_task: SWETask) -> _SWEAgentTask: Wraps the raw problem statement with SWE-Gym-style instructions that tell the agent about the ``answer`` tool. """ + answer_tool_enabled = _answer_tool_enabled() return _SWEAgentTask( - instruction=_wrap_instruction(swe_task.instruction), + instruction=_wrap_instruction( + swe_task.instruction, + hints_text=str((swe_task.metadata or {}).get("hints_text", "") or ""), + workdir=self._config.workdir, + answer_tool_enabled=answer_tool_enabled, + ), setup_shell=None, metadata={ "task_id": swe_task.task_id, @@ -702,22 +796,30 @@ def _grade_with_swegym_metadata( ) touched_files = self._extract_paths_from_test_patch(gym_task.test_patch) + # Also revert any test-like files the agent modified (anti-gaming). + changed_test_like_files = self._list_changed_test_paths(sandbox) + files_to_restore = sorted(set(touched_files) | set(changed_test_like_files)) self._revert_test_files( sandbox, base_commit=swe_task.base_commit, - paths=touched_files, + paths=files_to_restore, strict=True, ) self._apply_test_patch(sandbox, gym_task.test_patch) case_results = self._run_swegym_case_tests(sandbox, gym_task) - grade = grade_from_case_results(gym_task, case_results) + grade = grade_from_case_results( + gym_task, + case_results, + reward_mode=os.environ.get("SWE_REWARD_MODE", "binary").strip().lower() + or "binary", + ) # Best-effort cleanup in case grading was interrupted. self._revert_test_files( sandbox, base_commit=swe_task.base_commit, - paths=touched_files, + paths=files_to_restore, strict=False, ) @@ -785,6 +887,52 @@ def _extract_paths_from_test_patch(test_patch: str) -> list[str]: paths.append(path) return sorted(set(paths)) + @staticmethod + def _is_test_like_path(path: str) -> bool: + """Return True if path looks like a test file. + + Used to detect agent-modified test files before grading so they + can be reverted — prevents agents from gaming reward by editing + test expectations. + """ + text = (path or "").strip().strip('"') + if not text: + return False + normalized = text.replace("\\", "/") + basename = normalized.rsplit("/", 1)[-1] + if basename == "conftest.py": + return True + if basename.startswith("test_") or basename.endswith("_test.py"): + return True + parts = normalized.split("/") + return any(part in {"test", "tests", "testing"} for part in parts[:-1]) + + @classmethod + def _list_changed_test_paths( + cls, + sandbox: SandboxHandle, + ) -> list[str]: + """Detect test-like files the agent modified or created. + + Checks both tracked modifications (git diff) and untracked new + files (git ls-files --others). Results are filtered through + ``_is_test_like_path`` to only revert test-related files. + """ + commands = ( + "git diff --name-only HEAD --", + "git ls-files --others --exclude-standard", + ) + paths: set[str] = set() + for cmd in commands: + result = sandbox.exec(cmd, cwd=TESTBED, timeout=10) + if result.exit_code != 0: + continue + for line in (result.stdout or "").splitlines(): + path = line.strip() + if cls._is_test_like_path(path): + paths.add(path) + return sorted(paths) + def _revert_test_files( self, sandbox: SandboxHandle, diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index 441c2b9e6..b0c02835f 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -32,7 +32,7 @@ import time import uuid from dataclasses import dataclass, field -from typing import Any, Iterator, Sequence, cast +from typing import Any, Callable, Iterator, Sequence, cast import requests @@ -137,6 +137,16 @@ class WorkerConfig: # for the remainder of the training run. Prevents infinite retry # loops on tasks whose prompts inherently exceed the context window. max_context_overflow_per_task: int = 3 + # Message truncation settings for context window management. + # Instead of crashing on overflow, progressively truncate long tool + # outputs and assistant messages to fit within context budget. + max_tool_message_chars: int = 6000 + min_tool_message_chars: int = 256 + max_assistant_message_chars: int = 4000 + min_assistant_message_chars: int = 256 + # Retry config for transient sandbox/network errors. + max_rollout_attempts: int = 4 + failure_backoff_s: float = 30.0 # ── Worker ───────────────────────────────────────────────────────────── @@ -711,6 +721,20 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None # ── Tokenize this turn's full prompt ────────────── messages = _get_messages(intercept) tools = _get_tools(intercept) + + # Trim messages to fit within context budget before tokenizing. + messages, _ = _fit_messages_to_context_window( + messages=messages, + tools=tools, + render_prompt_ids=self._render_prompt_ids, + requested_completion_tokens=self._cfg.max_completion_tokens, + max_model_len=self._cfg.max_model_len, + max_tool_message_chars=self._cfg.max_tool_message_chars, + min_tool_message_chars=self._cfg.min_tool_message_chars, + max_assistant_message_chars=self._cfg.max_assistant_message_chars, + min_assistant_message_chars=self._cfg.min_assistant_message_chars, + ) + current_prompt_ids = self._render_prompt_ids(messages, tools) if initial_prompt_ids is None: @@ -887,6 +911,9 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None "agent_exit_after_terminal_stop", } ), + "request_idle_timeout": float( + rollout_stop_reason == "request_idle_timeout" + ), "wall_s": round(time.time() - t0, 3), "n_tokens": float(len(all_ids)), }, @@ -1232,3 +1259,200 @@ def _has_answer_call(resp: dict[str, Any]) -> bool: if ((tc or {}).get("function") or {}).get("name") == "answer": return True return False + + +# ── Context window message trimming ──────────────────────────────────── + +_TRUNCATION_MARKER = "\n...[truncated]...\n" +_OMITTED_TOOL_OUTPUT_MARKER = "[tool output omitted]" +_OMITTED_ASSISTANT_TEXT_MARKER = "[omitted]" + + +def _truncate_text_middle( + text: str, + *, + max_chars: int, + marker: str = _TRUNCATION_MARKER, +) -> tuple[str, bool]: + """Truncate text in the middle, preserving head and tail.""" + if max_chars <= 0 or len(text) <= max_chars: + return text, False + + usable = max_chars - len(marker) + if usable <= 8: + return marker[: max(1, max_chars)], True + + head = max(1, int(usable * 0.75)) + tail = max(1, usable - head) + return text[:head].rstrip() + marker + text[-tail:].lstrip(), True + + +def _truncate_messages_for_prompt_budget( + messages: Sequence[dict[str, Any]], + *, + max_tool_message_chars: int, + max_assistant_message_chars: int, +) -> tuple[list[dict[str, Any]], int, int]: + """Truncate long tool/assistant messages in-place.""" + truncated_messages: list[dict[str, Any]] = [] + tool_truncations = 0 + assistant_truncations = 0 + + for message in messages: + updated = dict(message) + content = updated.get("content") + if not isinstance(content, str): + truncated_messages.append(updated) + continue + + role = str(updated.get("role") or "") + if role == "tool": + content, changed = _truncate_text_middle( + content, + max_chars=max_tool_message_chars, + ) + if changed: + tool_truncations += 1 + elif role == "assistant": + content, changed = _truncate_text_middle( + content, + max_chars=max_assistant_message_chars, + ) + if changed: + assistant_truncations += 1 + + updated["content"] = content + truncated_messages.append(updated) + + return truncated_messages, tool_truncations, assistant_truncations + + +def _replace_oldest_message_content( + messages: Sequence[dict[str, Any]], + *, + role: str, + replacement: str, +) -> tuple[list[dict[str, Any]], bool]: + """Replace the oldest message of a given role with a short placeholder.""" + updated_messages = [dict(message) for message in messages] + for idx, message in enumerate(updated_messages): + if message.get("role") != role: + continue + content = message.get("content") + if not isinstance(content, str) or content == replacement: + continue + if len(replacement) >= len(content): + continue + message["content"] = replacement + updated_messages[idx] = message + return updated_messages, True + return updated_messages, False + + +def _fit_messages_to_context_window( + *, + messages: Sequence[dict[str, Any]], + tools: list[dict[str, Any]] | None, + render_prompt_ids: Callable[ + [list[dict[str, Any]], list[dict[str, Any]] | None], + list[int], + ], + requested_completion_tokens: int, + max_model_len: int, + max_tool_message_chars: int, + min_tool_message_chars: int, + max_assistant_message_chars: int, + min_assistant_message_chars: int, + safety_margin: int = 16, +) -> tuple[list[dict[str, Any]], list[int]]: + """Progressively truncate messages to fit prompt within context budget. + + Strategy: + 1. Truncate tool outputs to max_tool_message_chars, halving until min. + 2. Truncate assistant messages similarly. + 3. Replace oldest tool messages entirely with a placeholder. + 4. Replace oldest assistant messages entirely with a placeholder. + + Returns (trimmed_messages, prompt_token_ids). + """ + prompt_budget = max( + 1, + max_model_len - max(1, int(requested_completion_tokens)) - safety_margin, + ) + tool_char_budget = max(1, int(max_tool_message_chars)) + assistant_char_budget = max(1, int(max_assistant_message_chars)) + min_tool_chars = max(1, int(min_tool_message_chars)) + min_assistant_chars = max(1, int(min_assistant_message_chars)) + + base_messages = [dict(message) for message in messages] + prepared_messages = base_messages + prompt_ids = render_prompt_ids(prepared_messages, tools) + tool_truncations = 0 + assistant_truncations = 0 + + # Phase 1: progressive truncation + while True: + prepared_messages, tool_truncations, assistant_truncations = ( + _truncate_messages_for_prompt_budget( + base_messages, + max_tool_message_chars=tool_char_budget, + max_assistant_message_chars=assistant_char_budget, + ) + ) + prompt_ids = render_prompt_ids(prepared_messages, tools) + if len(prompt_ids) <= prompt_budget: + break + if tool_char_budget > min_tool_chars: + tool_char_budget = max(min_tool_chars, tool_char_budget // 2) + continue + if assistant_char_budget > min_assistant_chars: + assistant_char_budget = max( + min_assistant_chars, + assistant_char_budget // 2, + ) + continue + break + + # Phase 2: replace entire oldest messages with placeholders + omitted_tool_messages = 0 + omitted_assistant_messages = 0 + while len(prompt_ids) > prompt_budget: + prepared_messages, changed = _replace_oldest_message_content( + prepared_messages, + role="tool", + replacement=_OMITTED_TOOL_OUTPUT_MARKER, + ) + if changed: + omitted_tool_messages += 1 + prompt_ids = render_prompt_ids(prepared_messages, tools) + continue + + prepared_messages, changed = _replace_oldest_message_content( + prepared_messages, + role="assistant", + replacement=_OMITTED_ASSISTANT_TEXT_MARKER, + ) + if not changed: + break + omitted_assistant_messages += 1 + prompt_ids = render_prompt_ids(prepared_messages, tools) + + if ( + tool_truncations + or assistant_truncations + or omitted_tool_messages + or omitted_assistant_messages + ): + _log.info( + "trimmed intercepted prompt: prompt_tokens=%d/%d tool_truncations=%d " + "assistant_truncations=%d omitted_tool_messages=%d " + "omitted_assistant_messages=%d", + len(prompt_ids), + prompt_budget, + tool_truncations, + assistant_truncations, + omitted_tool_messages, + omitted_assistant_messages, + ) + + return prepared_messages, prompt_ids From 57e89eaba400a66ea5b476c5b772a7614df51c54 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sat, 30 May 2026 22:27:39 +0530 Subject: [PATCH 75/79] feat: LoRA config with multi GPU --- .../mini_swe_env/async_grpo/rollout_worker.py | 68 +++------ .../async_grpo/space_app/Dockerfile | 3 +- .../async_grpo/space_app/deploy_hf_space.sh | 2 +- .../async_grpo/space_app/start.sh | 21 ++- examples/mini_swe_env/train_swe_async_grpo.py | 134 +++++++++++++----- 5 files changed, 143 insertions(+), 85 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index b0c02835f..18a74ef45 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -66,26 +66,6 @@ _log = logging.getLogger(__name__) -def _vllm_version() -> tuple[int, ...]: - """Return parsed vLLM version as a tuple, e.g. (0, 20, 2). - - Returns (0, 0, 0) if vLLM is not installed or version cannot be parsed. - """ - try: - import vllm # noqa: F811 - - parts = vllm.__version__.split(".") - return tuple(int(p) for p in parts[:3]) - except Exception: - return (0, 0, 0) - - -# vLLM 0.21+ uses a four-phase weight transfer protocol: -# init_weight_transfer_engine → start_weight_update → update_weights → finish_weight_update -# start_weight_update(is_checkpoint_format=True) routes through model.load_weights() -# which handles parameter name remapping (e.g. CausalLM → VLM prefixes). -_VLLM_NEEDS_WEIGHT_UPDATE_LIFECYCLE = _vllm_version() >= (0, 21, 0) - # ── Sample dataclass ─────────────────────────────────────────────────── @@ -229,16 +209,9 @@ def __init__( # model architecture. For VLM models like Qwen3_5ForConditionalGeneration # served with --language-model-only, vLLM parameters are at # "language_model.model.layers.X" but the trainer (AutoModelForCausalLM) - # produces "model.layers.X". Set to "" to disable remapping. - # - # Only Qwen3.5 models need this prefix — they are unified VLMs where - # vLLM resolves Qwen3_5ForConditionalGeneration even in text-only mode. - # Standard CausalLM models (Qwen3, Llama, etc.) have matching param names. - self._vllm_weight_prefix = ( - "language_model." - if "qwen3.5" in vllm_model.lower() or "qwen3_5" in vllm_model.lower() - else "" - ) + # produces "model.layers.X". With is_checkpoint_format=True (vLLM 0.21+), + # vLLM's load_weights() handles this remapping automatically — no manual + # prefix is needed. self._init_weight_transfer() @@ -300,12 +273,10 @@ def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: names = [name for name, _ in items] - # VLM models (e.g. Qwen3_5ForConditionalGeneration) have parameters at - # language_model.model.layers.X but the trainer (AutoModelForCausalLM) - # produces names like model.layers.X. Prepend the prefix so names match. - if names and not names[0].startswith("language_model."): - if self._vllm_weight_prefix: - names = [f"{self._vllm_weight_prefix}{n}" for n in names] + # With is_checkpoint_format=True (vLLM 0.21+), vLLM's load_weights() + # handles the name remapping internally (e.g. model.layers.X → + # language_model.model.layers.X for VLM architectures like Qwen3.5). + # Send HuggingFace checkpoint-format names as-is. dtype_names = [ str(getattr(tensor, "dtype", "float32")).split(".")[-1] @@ -324,12 +295,11 @@ def send_weights(self, iterator: Iterator[tuple[str, Any]]) -> None: # start_weight_update(is_checkpoint_format=True) → update_weights → finish_weight_update # is_checkpoint_format=True tells vLLM to use model.load_weights() which # handles parameter name remapping (e.g. CausalLM → VLM prefixes). - if _VLLM_NEEDS_WEIGHT_UPDATE_LIFECYCLE: - self._post_json( - "/start_weight_update", - timeout=60, - json_body={"is_checkpoint_format": True}, - ) + self._post_json( + "/start_weight_update", + timeout=60, + json_body={"is_checkpoint_format": True}, + ) post_error: list[Exception] = [] @@ -364,14 +334,22 @@ def _post_update() -> None: f"vLLM /update_weights failed: {post_error[0]}" ) from post_error[0] - # vLLM >= 0.21: finalize layerwise reload / quantization. - if _VLLM_NEEDS_WEIGHT_UPDATE_LIFECYCLE: - self._post_json("/finish_weight_update", timeout=120) + # Finalize layerwise reload / quantization. + self._post_json("/finish_weight_update", timeout=120) def update_model_version(self, version: int) -> None: with self._lock: self._model_version = version + def check_health(self, stale_after_s: float) -> None: + """Health check called by the trainer when the queue is empty. + + Required by TRL's RolloutWorkerProtocol. Can be used to detect + stuck workers. Currently a no-op — our workers self-recover via + retry logic and idle backoff. + """ + pass + def _post_json( self, path: str, diff --git a/examples/mini_swe_env/async_grpo/space_app/Dockerfile b/examples/mini_swe_env/async_grpo/space_app/Dockerfile index de03cd1ca..3411fe8e5 100644 --- a/examples/mini_swe_env/async_grpo/space_app/Dockerfile +++ b/examples/mini_swe_env/async_grpo/space_app/Dockerfile @@ -37,7 +37,8 @@ RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ -e "." \ -e "envs/mini_swe_env" && \ uv pip install --system --no-cache \ - "trl>=1.5.1" \ + "trl @ git+https://github.com/rycerzes/trl@feat/async-grpo-parity" \ + "peft>=0.15.0" \ "accelerate>=1.13.0" \ "hf-sandbox[named-tunnels] @ git+https://github.com/rycerzes/hf-sandbox@feat/named-tunnels" \ "datasets>=4.8.0" \ diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index 591e7df08..011dbd2d5 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -197,7 +197,7 @@ variables = { "MAX_MODEL_LEN": "40960", "GPU_MEMORY_UTILIZATION": "0.75", "VLLM_GPU": "0", - "TRAINER_GPU": "1", + "TRAINER_GPU": os.environ.get("TRAINER_GPU", "1,2,3"), "INTERCEPTION_HOST": "0.0.0.0", "INTERCEPTION_PORT": "7860", "TRL_EXPERIMENTAL_SILENCE": "1", diff --git a/examples/mini_swe_env/async_grpo/space_app/start.sh b/examples/mini_swe_env/async_grpo/space_app/start.sh index 75feb2e17..5f28ade13 100755 --- a/examples/mini_swe_env/async_grpo/space_app/start.sh +++ b/examples/mini_swe_env/async_grpo/space_app/start.sh @@ -96,6 +96,21 @@ fi # ── 3. Start trainer (foreground) ────────────────────────────── echo "[start.sh] Starting trainer on GPU $TRAINER_GPU..." -CUDA_VISIBLE_DEVICES="$TRAINER_GPU" exec "$PYTHON_BIN" examples/mini_swe_env/train_swe_async_grpo.py \ - --vllm-url "http://127.0.0.1:${VLLM_PORT}" \ - "$@" + +# Count trainer GPUs for multi-GPU support +IFS=',' read -ra TRAINER_GPUS <<< "$TRAINER_GPU" +NUM_TRAINER_GPUS=${#TRAINER_GPUS[@]} + +if [ "$NUM_TRAINER_GPUS" -gt 1 ]; then + echo "[start.sh] Multi-GPU trainer: $NUM_TRAINER_GPUS GPUs ($TRAINER_GPU)" + CUDA_VISIBLE_DEVICES="$TRAINER_GPU" exec "$PYTHON_BIN" -m accelerate.commands.launch \ + --num_processes "$NUM_TRAINER_GPUS" \ + --mixed_precision bf16 \ + examples/mini_swe_env/train_swe_async_grpo.py \ + --vllm-url "http://127.0.0.1:${VLLM_PORT}" \ + "$@" +else + CUDA_VISIBLE_DEVICES="$TRAINER_GPU" exec "$PYTHON_BIN" examples/mini_swe_env/train_swe_async_grpo.py \ + --vllm-url "http://127.0.0.1:${VLLM_PORT}" \ + "$@" +fi diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 033cd897b..929f41a86 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -44,6 +44,12 @@ from transformers import AutoTokenizer # noqa: E402 from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer # noqa: E402 +try: + from peft import LoraConfig, TaskType # noqa: E402 +except ImportError: + LoraConfig = None # type: ignore[assignment,misc] + TaskType = None # type: ignore[assignment,misc] + from examples.mini_swe_env.async_grpo.control_plane import ( # noqa: E402 SWEAsyncControlPlane, SWEAsyncControlPlaneConfig, @@ -260,42 +266,54 @@ def main() -> int: tokenizer.pad_token = tokenizer.eos_token # ── Interception control plane (background thread) ──────────── - control_cfg = SWEAsyncControlPlaneConfig.from_env() - control_plane = SWEAsyncControlPlane(config=control_cfg) - server_loop, server_thread = start_interception_server(control_plane) - _log.info("InterceptionServer running in background thread") + # Only rank 0 owns the interception server, session factory, and rollout + # worker. Other ranks only participate in gradient computation. + local_rank = int(os.environ.get("LOCAL_RANK", "0")) + is_main = local_rank == 0 + + control_plane: SWEAsyncControlPlane | None = None + server_loop = None + server_thread = None + worker: SWERolloutWorker | None = None + + if is_main: + control_cfg = SWEAsyncControlPlaneConfig.from_env() + control_plane = SWEAsyncControlPlane(config=control_cfg) + server_loop, server_thread = start_interception_server(control_plane) + _log.info("InterceptionServer running in background thread") try: - # ── Session factory (Pi in sandbox) ─────────────────────── - backend = create_sandbox_backend(args.sandbox_backend) - session_factory = SWESessionFactory( - agent=args.agent, - config=SWEAgentConfig( - base_url=control_plane.interception_base_url, - api_key=control_plane.auth_token, - model=model, - agent_timeout_s=1800.0, - ), - sandbox_backend=backend, - mode="interception_gate", - interception_server=control_plane.server, - interception_base_url=control_plane.interception_base_url, - ) + # ── Session factory + rollout worker (rank 0 only) ──────── + if is_main: + assert control_plane is not None + backend = create_sandbox_backend(args.sandbox_backend) + session_factory = SWESessionFactory( + agent=args.agent, + config=SWEAgentConfig( + base_url=control_plane.interception_base_url, + api_key=control_plane.auth_token, + model=model, + agent_timeout_s=1800.0, + ), + sandbox_backend=backend, + mode="interception_gate", + interception_server=control_plane.server, + interception_base_url=control_plane.interception_base_url, + ) - # ── Rollout worker ──────────────────────────────────────── - worker = SWERolloutWorker( - session_factory=session_factory, - tasks=swe_tasks, - tokenizer=tokenizer, - vllm_base_url=vllm_url, - vllm_api_key=vllm_key, - vllm_model=model, - config=WorkerConfig( - max_inflight=2, - max_turns=args.max_turns, - num_generations=args.num_generations, - ), - ) + worker = SWERolloutWorker( + session_factory=session_factory, + tasks=swe_tasks, + tokenizer=tokenizer, + vllm_base_url=vllm_url, + vllm_api_key=vllm_key, + vllm_model=model, + config=WorkerConfig( + max_inflight=2, + max_turns=args.max_turns, + num_generations=args.num_generations, + ), + ) # ── Trainer ─────────────────────────────────────────────── def _noop_reward(**kwargs: Any) -> list[float]: @@ -312,6 +330,7 @@ def _noop_reward(**kwargs: Any) -> list[float]: ), "vllm_server_base_url": vllm_url, "vllm_server_timeout": 2400.0, + "model_init_kwargs": {"dtype": "bfloat16"}, "max_completion_length": 2048, "max_steps": args.max_steps, # Polar: rollout_batch_size=4. With single GPU trainer, @@ -345,12 +364,34 @@ def _noop_reward(**kwargs: Any) -> list[float]: ) resume_from_checkpoint = None + # ── LoRA config (Unsloth-recommended hyperparams for Qwen3.5) ── + peft_config = None + if LoraConfig is not None: + peft_config = LoraConfig( + task_type=TaskType.CAUSAL_LM, + r=16, + lora_alpha=16, # alpha == r recommended for Qwen3.5 + lora_dropout=0, + target_modules=[ + "q_proj", "k_proj", "v_proj", "o_proj", + "gate_proj", "up_proj", "down_proj", + ], + bias="none", + ) + _log.info( + "LoRA config: r=%d alpha=%d targets=%s", + peft_config.r, + peft_config.lora_alpha, + peft_config.target_modules, + ) + trainer = AsyncGRPOTrainer( model=model, reward_funcs=_noop_reward, train_dataset=dataset, processing_class=tokenizer, rollout_worker=worker, + peft_config=peft_config, args=AsyncGRPOConfig(**async_grpo_args), ) @@ -386,8 +427,31 @@ def _noop_reward(**kwargs: Any) -> list[float]: _log.info("done: step=%s", getattr(trainer.state, "global_step", "?")) return 0 finally: - stop_interception_server(control_plane, server_loop, server_thread) + if control_plane is not None and server_loop is not None and server_thread is not None: + stop_interception_server(control_plane, server_loop, server_thread) if __name__ == "__main__": - raise SystemExit(main()) + try: + raise SystemExit(main()) + except SystemExit as exc: + if exc.code == 0: + # Normal exit — sleep forever to prevent Space restart. + import time as _t + + _log.info("training completed successfully; sleeping to hold Space alive") + while True: + _t.sleep(3600) + else: + # Crash — log and sleep forever so logs are preserved. + _log.exception("training crashed (exit code %s); sleeping to preserve logs", exc.code) + import time as _t + + while True: + _t.sleep(3600) + except Exception: + _log.exception("unhandled exception in training; sleeping to preserve logs") + import time as _t + + while True: + _t.sleep(3600) From a0ab2e86ef80fd63a68a49eb5f161c26d0013cc5 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 31 May 2026 16:51:19 +0530 Subject: [PATCH 76/79] fix: fsdp2, adamw_torch and flash-linear-attn --- .../async_grpo/space_app/Dockerfile | 15 +++-- .../async_grpo/space_app/deploy_hf_space.sh | 28 +++++++-- examples/mini_swe_env/train_swe_async_grpo.py | 60 +++++++++++++++---- 3 files changed, 81 insertions(+), 22 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/space_app/Dockerfile b/examples/mini_swe_env/async_grpo/space_app/Dockerfile index 3411fe8e5..32e224a4a 100644 --- a/examples/mini_swe_env/async_grpo/space_app/Dockerfile +++ b/examples/mini_swe_env/async_grpo/space_app/Dockerfile @@ -28,9 +28,14 @@ COPY --chown=user . . # Install deps: # 1. Force-upgrade huggingface-hub (resolves vLLM vs hf-sandbox conflict). # 2. Force-install transformers >=5.2.0 (required by TRL v1.6.0 AsyncGRPO, -# and for native Qwen3.5-4B model type support). --no-deps avoids +# and for native Qwen3.5-4B model type support). --no-deps avoids # pulling vLLM's bundled transformers which may conflict. -# 3. TRL emits a warning about vLLM >0.18 but functions correctly — +# 3. Install Qwen3.5 fast-path kernel deps (latest known): +# - flash-linear-attention: provides `fla.ops.gated_delta_rule` +# NOTE: we intentionally do NOT install `causal-conv1d` in Spaces because +# its CUDA extension currently fails to load there (`libcudart.so.12`), +# which crashes model import. Qwen3.5 will fall back for conv1d. +# 4. TRL emits a warning about vLLM >0.18 but functions correctly — # the version check is advisory, not blocking. RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ uv pip install --system --no-cache --no-deps \ @@ -45,8 +50,10 @@ RUN uv pip install --system --no-cache "huggingface-hub>=1.5" && \ "aiohttp>=3.13.5" \ "fastmcp>=3.3.0" \ "trackio>=0.25.0" \ - "bitsandbytes>=0.43.0" && \ - uv pip install --system --no-cache --no-deps "transformers>=5.2.0" + "bitsandbytes>=0.43.0" \ + "flash-linear-attention>=0.5.0" && \ + uv pip install --system --no-cache --no-deps "transformers>=5.2.0" && \ + uv pip uninstall --system causal-conv1d || true # Runtime defaults ENV PYTHONPATH=$HOME/app/src:$HOME/app/envs \ diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index 011dbd2d5..8b5086e75 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -204,7 +204,7 @@ variables = { "TRACKIO_SPACE_ID": "rycerzes/swe-grpo-dashboard", "SWE_CHECKPOINT_TO_HUB": "1", "SWE_HUB_MODEL_ID": checkpoint_repo, - "SWE_RESUME_FROM_CHECKPOINT": "auto", + "SWE_RESUME_FROM_CHECKPOINT": "none", "SWE_CHECKPOINT_SAVE_STEPS": "2", "SWE_CHECKPOINT_SAVE_TOTAL_LIMIT": "2", "SWE_HUB_PRIVATE_REPO": "1", @@ -316,14 +316,32 @@ preload_from_hub: Model: \`$MODEL\` | Tasks: $MAX_TASKS | Steps: $MAX_STEPS | Backend: $SANDBOX_BACKEND EOF - # Patch start.sh to pass our training args + # Patch start.sh to pass our training args. + # NOTE: Multi-GPU branch uses line continuations, so patching only the + # `exec ...train_swe_async_grpo.py` line misses it. Instead, inject at the + # shared `--vllm-url ...` anchor used by both single- and multi-GPU paths. + TRAIN_ARGS="--sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS --num-generations $NUM_GENERATIONS" cat >> "$STAGE_DIR/start.sh" << EOF # Auto-generated training args from deploy script -# --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS +# $TRAIN_ARGS EOF - # Append deploy-time args to the trainer exec line (interpreter-agnostic). - sed -i "s|\(exec .*examples/mini_swe_env/train_swe_async_grpo.py\)|\1 --sandbox-backend $SANDBOX_BACKEND --max-tasks $MAX_TASKS --max-steps $MAX_STEPS --max-turns $MAX_TURNS --num-generations $NUM_GENERATIONS|" "$STAGE_DIR/start.sh" + + TRAIN_ARGS="$TRAIN_ARGS" "${PYTHON_CMD[@]}" << 'PYEOF' +from pathlib import Path +import os + +path = Path(os.environ["STAGE_DIR"]) / "start.sh" +text = path.read_text() +anchor = '--vllm-url "http://127.0.0.1:${VLLM_PORT}" \\\n' +replacement = f'{os.environ["TRAIN_ARGS"]} --vllm-url "http://127.0.0.1:${{VLLM_PORT}}" \\\n' +count = text.count(anchor) +if count == 0: + raise RuntimeError("Could not find trainer arg anchor in start.sh") +text = text.replace(anchor, replacement) +path.write_text(text) +print(f" ✓ Injected deploy args at {count} trainer command site(s)") +PYEOF FILE_COUNT=$(find "$STAGE_DIR" -type f | wc -l) echo " ✓ Staged $FILE_COUNT files" diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 929f41a86..57212feef 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -32,6 +32,7 @@ import os import sys import threading +from importlib import metadata as importlib_metadata from pathlib import Path from typing import Any @@ -42,6 +43,10 @@ from datasets import Dataset # noqa: E402 from transformers import AutoTokenizer # noqa: E402 +from transformers.utils.import_utils import ( # noqa: E402 + is_causal_conv1d_available, + is_flash_linear_attention_available, +) from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer # noqa: E402 try: @@ -155,6 +160,13 @@ def _int_env(name: str, default: int, *, min_value: int = 1) -> int: return value +def _pkg_version(dist_name: str) -> str: + try: + return importlib_metadata.version(dist_name) + except importlib_metadata.PackageNotFoundError: + return "not-installed" + + def _derive_checkpoint_repo_id() -> str | None: explicit = os.environ.get("SWE_HUB_MODEL_ID", "").strip() if explicit: @@ -244,6 +256,15 @@ def main() -> int: vllm_url = args.vllm_url vllm_key = os.environ.get("VLLM_API_KEY", "token").strip() + _log.info( + "qwen3.5 kernel deps: fla_available=%s causal_conv1d_available=%s fla_ver=%s causal_conv1d_ver=%s flash_attn_ver=%s", + is_flash_linear_attention_available(), + is_causal_conv1d_available(), + _pkg_version("flash-linear-attention"), + _pkg_version("causal-conv1d"), + _pkg_version("flash-attn"), + ) + # ── Load tasks ──────────────────────────────────────────────── gym_tasks = load_swegym_tasks(args.task_variant)[: args.max_tasks] swe_tasks = [t.to_swe_task() for t in gym_tasks] @@ -342,9 +363,21 @@ def _noop_reward(**kwargs: Any) -> list[float]: "learning_rate": 1e-6, "weight_decay": 0.1, "temperature": 1.0, - "optim": "adamw_bnb_8bit", + # bnb 8-bit optimizer is not yet compatible with FSDP2 DTensor step path. + # Use torch AdamW for multi-GPU FSDP2 stability. + "optim": "adamw_torch", "bf16": True, "gradient_checkpointing": True, + # FSDP2: make layer wrapping explicit so transformer class targeting is applied. + # Also force reshard_after_forward=True to avoid keeping full params resident. + "fsdp": "full_shard auto_wrap", + "fsdp_config": { + "fsdp_version": 2, + "fsdp_auto_wrap_policy": "TRANSFORMER_BASED_WRAP", + "fsdp_transformer_layer_cls_to_wrap": "Qwen3_5DecoderLayer", + "fsdp_reshard_after_forward": True, + }, + "ddp_timeout": 7200, "max_staleness": 4, "weight_sync_steps": 1, "max_inflight_tasks": 2, @@ -436,21 +469,22 @@ def _noop_reward(**kwargs: Any) -> list[float]: raise SystemExit(main()) except SystemExit as exc: if exc.code == 0: - # Normal exit — sleep forever to prevent Space restart. - import time as _t - - _log.info("training completed successfully; sleeping to hold Space alive") - while True: - _t.sleep(3600) + _log.info("training completed successfully") else: - # Crash — log and sleep forever so logs are preserved. - _log.exception("training crashed (exit code %s); sleeping to preserve logs", exc.code) - import time as _t + _log.error("training failed with exit code %s", exc.code) + except Exception: + _log.exception("unhandled exception in training") + + # Pause the Space to stop billing and prevent restart loops. + # Logs are preserved in the paused state for inspection. + try: + from huggingface_hub import HfApi as _HfApi - while True: - _t.sleep(3600) + _space_id = os.environ.get("SPACE_ID", "rycerzes/swe-async-grpo-train") + _HfApi().pause_space(_space_id) + _log.info("paused Space %s", _space_id) except Exception: - _log.exception("unhandled exception in training; sleeping to preserve logs") + _log.warning("failed to pause Space; sleeping to preserve logs") import time as _t while True: From c540b02b8224d870e68b462b3f4f453cde31cd0e Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Sun, 31 May 2026 20:59:33 +0530 Subject: [PATCH 77/79] chore: updated training parameters for SWE Async GRPO fix: max-turns arg --- .../mini_swe_env/async_grpo/rollout_worker.py | 5 ++-- .../async_grpo/space_app/README.md | 10 ++++--- .../async_grpo/space_app/deploy_hf_space.sh | 30 ++++++++++++------- examples/mini_swe_env/train_swe_async_grpo.py | 20 ++++++++----- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index 18a74ef45..91b1728c1 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -680,11 +680,12 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None turns = 0 answer_called = False pending_intercept: dict[str, Any] | None = None - rollout_stop_reason = "max_turns" + turn_limit: int | None = self._cfg.max_turns if self._cfg.max_turns > 0 else None + rollout_stop_reason = "max_turns" if turn_limit is not None else "running" t0 = time.time() try: - while turns < self._cfg.max_turns and not self._stop.is_set(): + while (turn_limit is None or turns < turn_limit) and not self._stop.is_set(): if pending_intercept is not None: intercept = pending_intercept pending_intercept = None diff --git a/examples/mini_swe_env/async_grpo/space_app/README.md b/examples/mini_swe_env/async_grpo/space_app/README.md index 036a765f1..3cbece430 100644 --- a/examples/mini_swe_env/async_grpo/space_app/README.md +++ b/examples/mini_swe_env/async_grpo/space_app/README.md @@ -5,10 +5,10 @@ colorFrom: blue colorTo: green sdk: docker app_port: 7860 -suggested_hardware: a10g-largex2 +suggested_hardware: a10g-largex4 startup_duration_timeout: 30m preload_from_hub: - - Qwen/Qwen3-1.7B + - Qwen/Qwen3.5-4B --- # SWE Async GRPO Training @@ -18,7 +18,8 @@ with Pi as the coding agent. **Architecture**: Pi runs in HF Sandboxes, its LLM calls are intercepted and forwarded to a co-located vLLM server for generation with exact -token IDs and logprobs. The trainer runs GRPO updates on a second GPU. +token IDs and logprobs. The trainer runs GRPO updates on the remaining +three GPUs (default `TRAINER_GPU=1,2,3`). See `SWE_ASYNC_GRPO_SPACE_DEPLOYMENT.md` in the repo for full details. @@ -30,13 +31,14 @@ Set these in the Space Settings tab: |--------|-------------| | `HF_TOKEN` | HF token for sandbox creation, model downloads, and checkpoint uploads | | `INTERCEPTION_AUTH_TOKEN` | Shared auth token for Pi ↔ InterceptionServer | -| `SWE_MODEL` | Model ID to serve and train (e.g. `Qwen/Qwen3-1.7B`) | +| `SWE_MODEL` | Model ID to serve and train (e.g. `Qwen/Qwen3.5-4B`) | ## Optional Variables (recommended) | Variable | Description | |----------|-------------| | `TRACKIO_SPACE_ID` | Trackio dashboard Space for metrics (e.g. `user/swe-grpo-dashboard`) | +| `SWE_REWARD_MODE` | Reward mode (`binary` recommended, default set by deploy script) | | `SWE_CHECKPOINT_TO_HUB` | Enable checkpoint upload to HF Hub (`1`/`0`, default `1` on Spaces) | | `SWE_HUB_MODEL_ID` | Hub model repo used as checkpoint bucket (e.g. `user/swe-async-grpo-checkpoints`) | | `SWE_RESUME_FROM_CHECKPOINT` | `auto` (default) to resume from `last-checkpoint`, or explicit path/name | diff --git a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh index 8b5086e75..0f95634ab 100755 --- a/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh +++ b/examples/mini_swe_env/async_grpo/space_app/deploy_hf_space.sh @@ -5,7 +5,7 @@ # 1. Prepares the Space directory (minimal repo subset) # 2. Creates/updates the HF Space # 3. Configures secrets and environment variables -# 4. Sets hardware (a10g-largex2) +# 4. Sets hardware (a10g-largex4) # 5. Pushes code and triggers build # 6. Monitors build/startup # @@ -19,11 +19,12 @@ # # Options: # --space-id OWNER/NAME Space ID (default: $HF_SPACE_ID or rycerzes/swe-async-grpo-train) -# --model MODEL_ID Model to train (default: Qwen/Qwen3-0.6B) -# --max-tasks N Number of SWE tasks (default: 5) -# --max-steps N Training steps (default: 10) -# --max-turns N Max agent turns per rollout (default: 30) -# --hardware HW Hardware tier (default: a10g-largex2) +# --model MODEL_ID Model to train (default: Qwen/Qwen3.5-4B) +# --max-tasks N Number of SWE tasks (default: 230) +# --max-steps N Training steps (default: 230) +# --max-turns N Max agent turns per rollout (default: 0, i.e. unlimited) +# --num-generations N Samples per prompt (default: 16) +# --hardware HW Hardware tier (default: a10g-largex4) # --sandbox-backend BACKEND Backend for agent (default: hf) # --skip-build Only configure, don't push code # --monitor Monitor logs after deploy @@ -37,12 +38,17 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)" SPACE_ID="${HF_SPACE_ID:-rycerzes/swe-async-grpo-train}" -MODEL="${SWE_MODEL:-Qwen/Qwen3-1.7B}" -MAX_TASKS=5 -MAX_STEPS=10 -MAX_TURNS=30 +# Default deployment profile for SWE-Gym Lite: +# - Base checkpoint: Qwen/Qwen3.5-4B +# - Samples per prompt: 16 +# - One full pass over lite split (230 tasks): max_tasks=230, max_steps=230 +# - Unlimited turns (max_turns=0) +MODEL="${SWE_MODEL:-Qwen/Qwen3.5-4B}" +MAX_TASKS=230 +MAX_STEPS=230 +MAX_TURNS=0 NUM_GENERATIONS=16 -HARDWARE="a10g-largex2" +HARDWARE="a10g-largex4" SANDBOX_BACKEND="hf" SKIP_BUILD=false MONITOR=false @@ -141,6 +147,7 @@ printf "║ %-54s ║\n" "Hardware: $HARDWARE" printf "║ %-54s ║\n" "Tasks: $MAX_TASKS" printf "║ %-54s ║\n" "Steps: $MAX_STEPS" printf "║ %-54s ║\n" "Turns: $MAX_TURNS" +printf "║ %-54s ║\n" "Gens: $NUM_GENERATIONS" printf "║ %-54s ║\n" "Sandbox: $SANDBOX_BACKEND" echo "╚══════════════════════════════════════════════════════════╝" echo "" @@ -202,6 +209,7 @@ variables = { "INTERCEPTION_PORT": "7860", "TRL_EXPERIMENTAL_SILENCE": "1", "TRACKIO_SPACE_ID": "rycerzes/swe-grpo-dashboard", + "SWE_REWARD_MODE": "binary", "SWE_CHECKPOINT_TO_HUB": "1", "SWE_HUB_MODEL_ID": checkpoint_repo, "SWE_RESUME_FROM_CHECKPOINT": "none", diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index 57212feef..d1e68a69a 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -122,16 +122,23 @@ def stop_interception_server( def _args() -> argparse.Namespace: p = argparse.ArgumentParser() + # Defaults target a full pass over SWE-Gym Lite. + # 230 tasks + 230 steps approximates one epoch on the lite split. p.add_argument("--task-variant", default="lite", choices=["lite", "full"]) - p.add_argument("--max-tasks", type=int, default=5) - p.add_argument("--max-steps", type=int, default=10) - p.add_argument("--max-turns", type=int, default=30) + p.add_argument("--max-tasks", type=int, default=230) + p.add_argument("--max-steps", type=int, default=230) + p.add_argument( + "--max-turns", + type=int, + default=0, + help="Max agent turns per rollout (0 means unlimited).", + ) p.add_argument("--sandbox-backend", default="hf", choices=["docker", "e2b", "hf"]) p.add_argument("--vllm-url", default="http://localhost:8000") p.add_argument("--agent", default="pi", choices=["pi", "opencode"]) p.add_argument( "--num-generations", type=int, default=16, - help="Rollouts per prompt for group-relative advantage (GRPO). Polar used 16.", + help="Rollouts per prompt for group-relative advantage (GRPO).", ) return p.parse_args() @@ -354,12 +361,11 @@ def _noop_reward(**kwargs: Any) -> list[float]: "model_init_kwargs": {"dtype": "bfloat16"}, "max_completion_length": 2048, "max_steps": args.max_steps, - # Polar: rollout_batch_size=4. With single GPU trainer, - # batch_size=4 via gradient accumulation. + # Effective train batch is 4 via gradient accumulation. "per_device_train_batch_size": 1, "gradient_accumulation_steps": 4, "num_generations": args.num_generations, - # Polar: lr=1e-6, weight_decay=0.1 + # Conservative optimizer settings for stability. "learning_rate": 1e-6, "weight_decay": 0.1, "temperature": 1.0, From 38482cd7f76c8f1af0ef4b6ed46cafbd770216b6 Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:52:44 +0530 Subject: [PATCH 78/79] chore: detection for running in HF Space and handle pausing logic chore: local run --- examples/mini_swe_env/pyproject.toml | 70 + examples/mini_swe_env/train_swe_async_grpo.py | 46 +- examples/mini_swe_env/uv.lock | 6054 +++++++++++++++++ 3 files changed, 6152 insertions(+), 18 deletions(-) create mode 100644 examples/mini_swe_env/pyproject.toml create mode 100644 examples/mini_swe_env/uv.lock diff --git a/examples/mini_swe_env/pyproject.toml b/examples/mini_swe_env/pyproject.toml new file mode 100644 index 000000000..49697ae83 --- /dev/null +++ b/examples/mini_swe_env/pyproject.toml @@ -0,0 +1,70 @@ +# Training dependencies for mini_swe_env async GRPO example. +# +# Install: +# cd examples/mini_swe_env +# uv sync +# +# Or from repo root: +# uv sync --project examples/mini_swe_env +# +# This installs everything needed to run train_swe_async_grpo.py locally +# on hosted compute (Slurm, bare-metal, cloud VMs) with HF Sandbox for +# agent execution. + +[project] +name = "mini-swe-train" +version = "0.1.0" +description = "SWE Async GRPO training — local/hosted compute launcher" +requires-python = ">=3.12" +dependencies = [ + "openenv-core>=0.3.0", + "openenv-mini-swe-env>=0.1.0", + # TRL fork with AsyncGRPOTrainer + async rollout parity + "trl @ git+https://github.com/rycerzes/trl@feat/async-grpo-parity", + # transformers >=5.2.0 required for native Qwen3.5 model support. + # Installed --no-deps to avoid pulling conflicting transitive deps. + "transformers>=5.2.0", + "accelerate>=1.13.0", + "peft>=0.15.0", + "datasets>=4.8.0", + "bitsandbytes>=0.43.0", + + # vLLM 0.21+ for Qwen3.5 support + NCCL weight transfer protocol. + # NOTE: on systems where vllm is installed via pip (not the Docker + # image), uncomment the line below or install separately: + # "vllm>=0.21.0", + + "hf-sandbox @ git+https://github.com/rycerzes/hf-sandbox@feat/named-tunnels", + + "aiohttp>=3.13.5", + "fastmcp>=3.3.0", + "requests>=2.25.0", + "huggingface-hub>=1.5", + + "trackio>=0.25.0", + + "flash-linear-attention>=0.5.0", +] + +[project.optional-dependencies] +# vLLM is large and often installed system-wide or via container. +# Include explicitly when needed. +vllm = ["vllm>=0.21.0"] + +dev = [ + "pytest>=8.0.0", + "pytest-asyncio>=0.23.0", + "ruff>=0.14.0", +] + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.uv] +# Local path dependencies — resolved relative to this file. +# openenv-core from repo root, mini_swe_env from envs/. + +[tool.uv.sources] +openenv-core = { path = "../../", editable = true } +openenv-mini-swe-env = { path = "../../envs/mini_swe_env", editable = true } \ No newline at end of file diff --git a/examples/mini_swe_env/train_swe_async_grpo.py b/examples/mini_swe_env/train_swe_async_grpo.py index d1e68a69a..492e71fc4 100644 --- a/examples/mini_swe_env/train_swe_async_grpo.py +++ b/examples/mini_swe_env/train_swe_async_grpo.py @@ -174,6 +174,15 @@ def _pkg_version(dist_name: str) -> str: return "not-installed" +def _is_running_in_space() -> bool: + """Detect if we're running inside an HF Space (vs local/Slurm/cloud).""" + return bool( + os.environ.get("SPACE_HOST") + or os.environ.get("HF_SPACE_ID") + or os.environ.get("SPACE_ID") + ) + + def _derive_checkpoint_repo_id() -> str | None: explicit = os.environ.get("SWE_HUB_MODEL_ID", "").strip() if explicit: @@ -188,12 +197,7 @@ def _derive_checkpoint_repo_id() -> str | None: def _build_checkpoint_args() -> tuple[dict[str, Any], str | None, bool]: - in_space = bool( - os.environ.get("HF_SPACE_ID") - or os.environ.get("SPACE_ID") - or os.environ.get("SPACE_HOST") - ) - enabled = _bool_env("SWE_CHECKPOINT_TO_HUB", default=in_space) + enabled = _bool_env("SWE_CHECKPOINT_TO_HUB", default=_is_running_in_space()) if not enabled: return {}, None, False @@ -471,27 +475,33 @@ def _noop_reward(**kwargs: Any) -> list[float]: if __name__ == "__main__": + _exit_code: int | None = None try: raise SystemExit(main()) except SystemExit as exc: + _exit_code = exc.code # type: ignore[assignment] if exc.code == 0: _log.info("training completed successfully") else: _log.error("training failed with exit code %s", exc.code) except Exception: + _exit_code = 1 _log.exception("unhandled exception in training") - # Pause the Space to stop billing and prevent restart loops. - # Logs are preserved in the paused state for inspection. - try: - from huggingface_hub import HfApi as _HfApi + # When running inside an HF Space, pause it to stop billing and + # prevent restart loops. On local/Slurm runs this is a no-op. + if _is_running_in_space(): + try: + from huggingface_hub import HfApi as _HfApi - _space_id = os.environ.get("SPACE_ID", "rycerzes/swe-async-grpo-train") - _HfApi().pause_space(_space_id) - _log.info("paused Space %s", _space_id) - except Exception: - _log.warning("failed to pause Space; sleeping to preserve logs") - import time as _t + _space_id = os.environ.get("SPACE_ID", "rycerzes/swe-async-grpo-train") + _HfApi().pause_space(_space_id) + _log.info("paused Space %s", _space_id) + except Exception: + _log.warning("failed to pause Space; sleeping to preserve logs") + import time as _t + + while True: + _t.sleep(3600) - while True: - _t.sleep(3600) + raise SystemExit(_exit_code or 0) diff --git a/examples/mini_swe_env/uv.lock b/examples/mini_swe_env/uv.lock new file mode 100644 index 000000000..cca57a93d --- /dev/null +++ b/examples/mini_swe_env/uv.lock @@ -0,0 +1,6054 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "accelerate" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, +] + +[[package]] +name = "aiofile" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "caio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/41/2fea7e193e061ce54eacc3b7bc0e6a99e4fcff43c78cf0a76dd781ed8334/aiofile-3.11.1.tar.gz", hash = "sha256:1f91912c6643d2a4e49ca4ae3514f0bf3867ce948a36d99a6411b8f4755f4cf9", size = 19342, upload-time = "2026-05-16T08:18:33.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/cd/0d76dfc5de72bde52f55f53e925c7d152d9c7906634ec1e0cbc7e8d4ad93/aiofile-3.11.1-py3-none-any.whl", hash = "sha256:ce77d14ac07f77bc2b757834a5c129321f3f705c474593deed5ab209079a52c9", size = 20446, upload-time = "2026-05-16T08:18:32.051Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anthropic" +version = "0.105.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/46/47581b8c689c743ceabf6a0f9ff48472160900ce802d26c0fb50423997b3/anthropic-0.105.2.tar.gz", hash = "sha256:0e26b90841c2dced7cc6e98d21d5517d0be33f1876b8e779f478202e28bcaa07", size = 853789, upload-time = "2026-05-29T00:21:14.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/75/be0c357e33a5a56c8f9db5b4212f886138d2bf59c0952d858f6b75d710ef/anthropic-0.105.2-py3-none-any.whl", hash = "sha256:e53ed5f6bf36fb1ecb9b25d8634cfd30e02fab9fb3374a0c2d5c585874757230", size = 837507, upload-time = "2026-05-29T00:21:15.528Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "apache-tvm-ffi" +version = "0.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/60/1e787a0b5ebf318483235be2a689ee367173983067e441b8379564f667c0/apache_tvm_ffi-0.1.9.tar.gz", hash = "sha256:d2d402587e8906de0a07f4746aa78f3d452c7efe3625d4bb39ac2ad693bce530", size = 2513731, upload-time = "2026-02-27T19:28:06.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/f2/b8c4b151169f6d7ba8773c8af68b2e0c1013d7fb3f1bdf87573f47157ce9/apache_tvm_ffi-0.1.9-cp312-abi3-macosx_11_0_arm64.whl", hash = "sha256:49e52350b0470654847de752e65603b604a4d3323e7e9f5e8a982f44acc4c143", size = 2041756, upload-time = "2026-02-27T19:27:23.931Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c0/6d3d54f50012255b41bc3e24944c086f63c4707c8686c7c6780e9283eb96/apache_tvm_ffi-0.1.9-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d503029e66c43b1a1cb1a42a1e9bb428c8a28dcbdec31c28e705472ca648a3a", size = 2203712, upload-time = "2026-02-27T19:27:25.867Z" }, + { url = "https://files.pythonhosted.org/packages/c6/dd/2bab4c6cd86257dbf99e93452a1af833113f8dc3e25a25579f6e4e4c8a94/apache_tvm_ffi-0.1.9-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28241371934ea8af10d5067087ba1229ebddded7b2c02d33a258ec2a96df8c46", size = 2299704, upload-time = "2026-02-27T19:27:27.477Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4a/b469bcb2e1014cb84d336d2a59f42958a058251c577a4c2680cacad346e2/apache_tvm_ffi-0.1.9-cp312-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:87cacce81df55685fc6a76e1e3c5db1200e85e87bf5974b692c59d131b7bc622", size = 2130865, upload-time = "2026-02-27T19:27:29.092Z" }, + { url = "https://files.pythonhosted.org/packages/70/ef/5402da5d37f5270fd88ea0348acca78dba9be8bdbf6c2bcae0935eb03ef1/apache_tvm_ffi-0.1.9-cp312-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f45eb43499acac45ff6c93564f0ff2d3ca27b69656d540fd56ce59d51c0b4c65", size = 2278991, upload-time = "2026-02-27T19:27:30.729Z" }, + { url = "https://files.pythonhosted.org/packages/b5/23/1b7dc5f0807f83098183a57db6ee85b2c93b646d74a6e03781c9208aaeb0/apache_tvm_ffi-0.1.9-cp312-abi3-win_amd64.whl", hash = "sha256:d1dcf4c041d5ec05e3da1d545800c33cdbb95c113baa7705085ff79fa262752b", size = 1973200, upload-time = "2026-02-27T19:27:32.367Z" }, + { url = "https://files.pythonhosted.org/packages/4a/1e/991ae65e64ce132c1ba665562db6638f5696d6133f580e20c653de33b9af/apache_tvm_ffi-0.1.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c3349f72ddb8ce206472d0380a729f213017a2180707096f8d57114b81097dd1", size = 2072944, upload-time = "2026-02-27T19:27:34.261Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a7/1e0643949e683fb3cfababd87058c0cfef122d1a3bb6ce703f719051b842/apache_tvm_ffi-0.1.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d1f4d2b7ec7b1213632e9a104e9330bfc3dec48decffa62114c33aa188c9f43a", size = 2215954, upload-time = "2026-02-27T19:27:35.872Z" }, + { url = "https://files.pythonhosted.org/packages/d6/06/5016191ab61d2db4c3a7d754a3c1184e0836f575a7d08491669738c5e4b9/apache_tvm_ffi-0.1.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e4f01d16ba53fe118e363f7257253f07003797e4abe6fc9567f23b6a930dbff2", size = 2307291, upload-time = "2026-02-27T19:27:37.527Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f5/40bf0667330938efbfc0a51743cc53c79e41b4ece1a8abad3076192c9674/apache_tvm_ffi-0.1.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c0581dd6bfbce7b017ef85cfda08bbe38891cc4b3afbcfaa8bc2d383728e426", size = 2143850, upload-time = "2026-02-27T19:27:40.437Z" }, + { url = "https://files.pythonhosted.org/packages/72/4a/421cbd4ed32e8bad3b88af3e8fa145c1f6f493bdd05be15b6f2d9b3cb7d6/apache_tvm_ffi-0.1.9-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dfa14be2a49347791ef21222a8225ce7f99bfec17104a676cb4f1bf3a107088", size = 2289038, upload-time = "2026-02-27T19:27:41.972Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1a/c8923d819b49872a612033b90d29299c0be73a7cbed1ddb3dc78dfe5e9f1/apache_tvm_ffi-0.1.9-cp314-cp314t-win_amd64.whl", hash = "sha256:a42d7ca27dce83efbdce7ec970fe3e773a69c31d928730ee5d9badb1229d106c", size = 2039007, upload-time = "2026-02-27T19:27:43.618Z" }, +] + +[[package]] +name = "astor" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/21/75b771132fee241dfe601d39ade629548a9626d1d39f333fde31bc46febe/astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e", size = 35090, upload-time = "2019-12-10T01:50:35.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/88/97eef84f48fa04fbd6750e62dcceafba6c63c81b7ac1420856c8dcc0a3f9/astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", size = 27488, upload-time = "2019-12-10T01:50:33.628Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "audioop-lts" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/53/946db57842a50b2da2e0c1e34bd37f36f5aadba1a929a3971c5d7841dbca/audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0", size = 30686, upload-time = "2025-08-05T16:43:17.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/d4/94d277ca941de5a507b07f0b592f199c22454eeaec8f008a286b3fbbacd6/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800", size = 46523, upload-time = "2025-08-05T16:42:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5a/656d1c2da4b555920ce4177167bfeb8623d98765594af59702c8873f60ec/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303", size = 27455, upload-time = "2025-08-05T16:42:22.283Z" }, + { url = "https://files.pythonhosted.org/packages/1b/83/ea581e364ce7b0d41456fb79d6ee0ad482beda61faf0cab20cbd4c63a541/audioop_lts-0.2.2-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75", size = 26997, upload-time = "2025-08-05T16:42:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/e8964210b5e216e5041593b7d33e97ee65967f17c282e8510d19c666dab4/audioop_lts-0.2.2-cp313-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d", size = 85844, upload-time = "2025-08-05T16:42:25.208Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2e/0a1c52faf10d51def20531a59ce4c706cb7952323b11709e10de324d6493/audioop_lts-0.2.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b", size = 85056, upload-time = "2025-08-05T16:42:26.559Z" }, + { url = "https://files.pythonhosted.org/packages/75/e8/cd95eef479656cb75ab05dfece8c1f8c395d17a7c651d88f8e6e291a63ab/audioop_lts-0.2.2-cp313-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8", size = 93892, upload-time = "2025-08-05T16:42:27.902Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1e/a0c42570b74f83efa5cca34905b3eef03f7ab09fe5637015df538a7f3345/audioop_lts-0.2.2-cp313-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc", size = 96660, upload-time = "2025-08-05T16:42:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/8a0ae607ca07dbb34027bac8db805498ee7bfecc05fd2c148cc1ed7646e7/audioop_lts-0.2.2-cp313-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3", size = 79143, upload-time = "2025-08-05T16:42:29.929Z" }, + { url = "https://files.pythonhosted.org/packages/12/17/0d28c46179e7910bfb0bb62760ccb33edb5de973052cb2230b662c14ca2e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6", size = 84313, upload-time = "2025-08-05T16:42:30.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/ba/bd5d3806641564f2024e97ca98ea8f8811d4e01d9b9f9831474bc9e14f9e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a", size = 93044, upload-time = "2025-08-05T16:42:31.959Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5e/435ce8d5642f1f7679540d1e73c1c42d933331c0976eb397d1717d7f01a3/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623", size = 78766, upload-time = "2025-08-05T16:42:33.302Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3b/b909e76b606cbfd53875693ec8c156e93e15a1366a012f0b7e4fb52d3c34/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7", size = 87640, upload-time = "2025-08-05T16:42:34.854Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/8f1603b4572d79b775f2140d7952f200f5e6c62904585d08a01f0a70393a/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449", size = 86052, upload-time = "2025-08-05T16:42:35.839Z" }, + { url = "https://files.pythonhosted.org/packages/b5/96/c37846df657ccdda62ba1ae2b6534fa90e2e1b1742ca8dcf8ebd38c53801/audioop_lts-0.2.2-cp313-abi3-win32.whl", hash = "sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636", size = 26185, upload-time = "2025-08-05T16:42:37.04Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/9d78fdb5b844a83da8a71226c7bdae7cc638861085fff7a1d707cb4823fa/audioop_lts-0.2.2-cp313-abi3-win_amd64.whl", hash = "sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e", size = 30503, upload-time = "2025-08-05T16:42:38.427Z" }, + { url = "https://files.pythonhosted.org/packages/34/25/20d8fde083123e90c61b51afb547bb0ea7e77bab50d98c0ab243d02a0e43/audioop_lts-0.2.2-cp313-abi3-win_arm64.whl", hash = "sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f", size = 24173, upload-time = "2025-08-05T16:42:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/58/a7/0a764f77b5c4ac58dc13c01a580f5d32ae8c74c92020b961556a43e26d02/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09", size = 47096, upload-time = "2025-08-05T16:42:40.684Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ed/ebebedde1a18848b085ad0fa54b66ceb95f1f94a3fc04f1cd1b5ccb0ed42/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58", size = 27748, upload-time = "2025-08-05T16:42:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/cb/6e/11ca8c21af79f15dbb1c7f8017952ee8c810c438ce4e2b25638dfef2b02c/audioop_lts-0.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19", size = 27329, upload-time = "2025-08-05T16:42:42.987Z" }, + { url = "https://files.pythonhosted.org/packages/84/52/0022f93d56d85eec5da6b9da6a958a1ef09e80c39f2cc0a590c6af81dcbb/audioop_lts-0.2.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911", size = 92407, upload-time = "2025-08-05T16:42:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/87/1d/48a889855e67be8718adbc7a01f3c01d5743c325453a5e81cf3717664aad/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9", size = 91811, upload-time = "2025-08-05T16:42:45.325Z" }, + { url = "https://files.pythonhosted.org/packages/98/a6/94b7213190e8077547ffae75e13ed05edc488653c85aa5c41472c297d295/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe", size = 100470, upload-time = "2025-08-05T16:42:46.468Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/78450d7cb921ede0cfc33426d3a8023a3bda755883c95c868ee36db8d48d/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132", size = 103878, upload-time = "2025-08-05T16:42:47.576Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e2/cd5439aad4f3e34ae1ee852025dc6aa8f67a82b97641e390bf7bd9891d3e/audioop_lts-0.2.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753", size = 84867, upload-time = "2025-08-05T16:42:49.003Z" }, + { url = "https://files.pythonhosted.org/packages/68/4b/9d853e9076c43ebba0d411e8d2aa19061083349ac695a7d082540bad64d0/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb", size = 90001, upload-time = "2025-08-05T16:42:50.038Z" }, + { url = "https://files.pythonhosted.org/packages/58/26/4bae7f9d2f116ed5593989d0e521d679b0d583973d203384679323d8fa85/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093", size = 99046, upload-time = "2025-08-05T16:42:51.111Z" }, + { url = "https://files.pythonhosted.org/packages/b2/67/a9f4fb3e250dda9e9046f8866e9fa7d52664f8985e445c6b4ad6dfb55641/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7", size = 84788, upload-time = "2025-08-05T16:42:52.198Z" }, + { url = "https://files.pythonhosted.org/packages/70/f7/3de86562db0121956148bcb0fe5b506615e3bcf6e63c4357a612b910765a/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c", size = 94472, upload-time = "2025-08-05T16:42:53.59Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/fd772bf9078ae1001207d2df1eef3da05bea611a87dd0e8217989b2848fa/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5", size = 92279, upload-time = "2025-08-05T16:42:54.632Z" }, + { url = "https://files.pythonhosted.org/packages/4f/41/affea7181592ab0ab560044632571a38edaf9130b84928177823fbf3176a/audioop_lts-0.2.2-cp313-cp313t-win32.whl", hash = "sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917", size = 26568, upload-time = "2025-08-05T16:42:55.627Z" }, + { url = "https://files.pythonhosted.org/packages/28/2b/0372842877016641db8fc54d5c88596b542eec2f8f6c20a36fb6612bf9ee/audioop_lts-0.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547", size = 30942, upload-time = "2025-08-05T16:42:56.674Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/baf2b9cc7e96c179bb4a54f30fcd83e6ecb340031bde68f486403f943768/audioop_lts-0.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969", size = 24603, upload-time = "2025-08-05T16:42:57.571Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/413b5a2804091e2c7d5def1d618e4837f1cb82464e230f827226278556b7/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6", size = 47104, upload-time = "2025-08-05T16:42:58.518Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/daa3308dc6593944410c2c68306a5e217f5c05b70a12e70228e7dd42dc5c/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a", size = 27754, upload-time = "2025-08-05T16:43:00.132Z" }, + { url = "https://files.pythonhosted.org/packages/4e/86/c2e0f627168fcf61781a8f72cab06b228fe1da4b9fa4ab39cfb791b5836b/audioop_lts-0.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b", size = 27332, upload-time = "2025-08-05T16:43:01.666Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/35dce665255434f54e5307de39e31912a6f902d4572da7c37582809de14f/audioop_lts-0.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6", size = 92396, upload-time = "2025-08-05T16:43:02.991Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d2/deeb9f51def1437b3afa35aeb729d577c04bcd89394cb56f9239a9f50b6f/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf", size = 91811, upload-time = "2025-08-05T16:43:04.096Z" }, + { url = "https://files.pythonhosted.org/packages/76/3b/09f8b35b227cee28cc8231e296a82759ed80c1a08e349811d69773c48426/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd", size = 100483, upload-time = "2025-08-05T16:43:05.085Z" }, + { url = "https://files.pythonhosted.org/packages/0b/15/05b48a935cf3b130c248bfdbdea71ce6437f5394ee8533e0edd7cfd93d5e/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a", size = 103885, upload-time = "2025-08-05T16:43:06.197Z" }, + { url = "https://files.pythonhosted.org/packages/83/80/186b7fce6d35b68d3d739f228dc31d60b3412105854edb975aa155a58339/audioop_lts-0.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e", size = 84899, upload-time = "2025-08-05T16:43:07.291Z" }, + { url = "https://files.pythonhosted.org/packages/49/89/c78cc5ac6cb5828f17514fb12966e299c850bc885e80f8ad94e38d450886/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7", size = 89998, upload-time = "2025-08-05T16:43:08.335Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/6401888d0c010e586c2ca50fce4c903d70a6bb55928b16cfbdfd957a13da/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5", size = 99046, upload-time = "2025-08-05T16:43:09.367Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/c874ca9bb447dae0e2ef2e231f6c4c2b0c39e31ae684d2420b0f9e97ee68/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9", size = 84843, upload-time = "2025-08-05T16:43:10.749Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/0323e66f3daebc13fd46b36b30c3be47e3fc4257eae44f1e77eb828c703f/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602", size = 94490, upload-time = "2025-08-05T16:43:12.131Z" }, + { url = "https://files.pythonhosted.org/packages/98/6b/acc7734ac02d95ab791c10c3f17ffa3584ccb9ac5c18fd771c638ed6d1f5/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0", size = 92297, upload-time = "2025-08-05T16:43:13.139Z" }, + { url = "https://files.pythonhosted.org/packages/13/c3/c3dc3f564ce6877ecd2a05f8d751b9b27a8c320c2533a98b0c86349778d0/audioop_lts-0.2.2-cp314-cp314t-win32.whl", hash = "sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3", size = 27331, upload-time = "2025-08-05T16:43:14.19Z" }, + { url = "https://files.pythonhosted.org/packages/72/bb/b4608537e9ffcb86449091939d52d24a055216a36a8bf66b936af8c3e7ac/audioop_lts-0.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b", size = 31697, upload-time = "2025-08-05T16:43:15.193Z" }, + { url = "https://files.pythonhosted.org/packages/f6/22/91616fe707a5c5510de2cac9b046a30defe7007ba8a0c04f9c08f27df312/audioop_lts-0.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd", size = 25206, upload-time = "2025-08-05T16:43:16.444Z" }, +] + +[[package]] +name = "authlib" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "joserfc" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511, upload-time = "2026-05-06T08:10:23.116Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" }, +] + +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + +[[package]] +name = "bitsandbytes" +version = "0.49.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "packaging" }, + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/7d/f1fe0992334b18cd8494f89aeec1dcc674635584fcd9f115784fea3a1d05/bitsandbytes-0.49.2-py3-none-macosx_14_0_arm64.whl", hash = "sha256:87be5975edeac5396d699ecbc39dfc47cf2c026daaf2d5852a94368611a6823f", size = 131940, upload-time = "2026-02-16T21:26:04.572Z" }, + { url = "https://files.pythonhosted.org/packages/29/71/acff7af06c818664aa87ff73e17a52c7788ad746b72aea09d3cb8e424348/bitsandbytes-0.49.2-py3-none-manylinux_2_24_aarch64.whl", hash = "sha256:2fc0830c5f7169be36e60e11f2be067c8f812dfcb829801a8703735842450750", size = 31442815, upload-time = "2026-02-16T21:26:06.783Z" }, + { url = "https://files.pythonhosted.org/packages/19/57/3443d6f183436fbdaf5000aac332c4d5ddb056665d459244a5608e98ae92/bitsandbytes-0.49.2-py3-none-manylinux_2_24_x86_64.whl", hash = "sha256:54b771f06e1a3c73af5c7f16ccf0fc23a846052813d4b008d10cb6e017dd1c8c", size = 60651714, upload-time = "2026-02-16T21:26:11.579Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d4/501655842ad6771fb077f576d78cbedb5445d15b1c3c91343ed58ca46f0e/bitsandbytes-0.49.2-py3-none-win_amd64.whl", hash = "sha256:2e0ddd09cd778155388023cbe81f00afbb7c000c214caef3ce83386e7144df7d", size = 55372289, upload-time = "2026-02-16T21:26:16.267Z" }, +] + +[[package]] +name = "blake3" +version = "1.0.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/aa/abcd75e9600987a0bc6cfe9b6b2ff3f0e2cb08c170addc6e76035b5c4cb3/blake3-1.0.8.tar.gz", hash = "sha256:513cc7f0f5a7c035812604c2c852a0c1468311345573de647e310aca4ab165ba", size = 117308, upload-time = "2025-10-14T06:47:48.83Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/a0/b7b6dff04012cfd6e665c09ee446f749bd8ea161b00f730fe1bdecd0f033/blake3-1.0.8-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d8da4233984d51471bd4e4366feda1d90d781e712e0a504ea54b1f2b3577557b", size = 347983, upload-time = "2025-10-14T06:45:47.214Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a2/264091cac31d7ae913f1f296abc20b8da578b958ffb86100a7ce80e8bf5c/blake3-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1257be19f2d381c868a34cc822fc7f12f817ddc49681b6d1a2790bfbda1a9865", size = 325415, upload-time = "2025-10-14T06:45:48.482Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/85a4c0782f613de23d114a7a78fcce270f75b193b3ff3493a0de24ba104a/blake3-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:269f255b110840e52b6ce9db02217e39660ebad3e34ddd5bca8b8d378a77e4e1", size = 371296, upload-time = "2025-10-14T06:45:49.674Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/488475254976ed93fab57c67aa80d3b40df77f7d9db6528c9274bff53e08/blake3-1.0.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66ca28a673025c40db3eba21a9cac52f559f83637efa675b3f6bd8683f0415f3", size = 374516, upload-time = "2025-10-14T06:45:51.23Z" }, + { url = "https://files.pythonhosted.org/packages/7b/21/2a1c47fedb77fb396512677ec6d46caf42ac6e9a897db77edd0a2a46f7bb/blake3-1.0.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb04966537777af56c1f399b35525aa70a1225816e121ff95071c33c0f7abca", size = 447911, upload-time = "2025-10-14T06:45:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7d/db0626df16029713e7e61b67314c4835e85c296d82bd907c21c6ea271da2/blake3-1.0.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5b5da177d62cc4b7edf0cea08fe4dec960c9ac27f916131efa890a01f747b93", size = 505420, upload-time = "2025-10-14T06:45:54.445Z" }, + { url = "https://files.pythonhosted.org/packages/5b/55/6e737850c2d58a6d9de8a76dad2ae0f75b852a23eb4ecb07a0b165e6e436/blake3-1.0.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:38209b10482c97e151681ea3e91cc7141f56adbbf4820a7d701a923124b41e6a", size = 394189, upload-time = "2025-10-14T06:45:55.719Z" }, + { url = "https://files.pythonhosted.org/packages/5b/94/eafaa5cdddadc0c9c603a6a6d8339433475e1a9f60c8bb9c2eed2d8736b6/blake3-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504d1399b7fb91dfe5c25722d2807990493185faa1917456455480c36867adb5", size = 388001, upload-time = "2025-10-14T06:45:57.067Z" }, + { url = "https://files.pythonhosted.org/packages/17/81/735fa00d13de7f68b25e1b9cb36ff08c6f165e688d85d8ec2cbfcdedccc5/blake3-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c84af132aa09abeadf9a0118c8fb26f4528f3f42c10ef8be0fcf31c478774ec4", size = 550302, upload-time = "2025-10-14T06:45:58.657Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c6/d1fe8bdea4a6088bd54b5a58bc40aed89a4e784cd796af7722a06f74bae7/blake3-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a25db3d36b55f5ed6a86470155cc749fc9c5b91c949b8d14f48658f9d960d9ec", size = 554211, upload-time = "2025-10-14T06:46:00.269Z" }, + { url = "https://files.pythonhosted.org/packages/55/d1/ca74aa450cbe10e396e061f26f7a043891ffa1485537d6b30d3757e20995/blake3-1.0.8-cp312-cp312-win32.whl", hash = "sha256:e0fee93d5adcd44378b008c147e84f181f23715307a64f7b3db432394bbfce8b", size = 228343, upload-time = "2025-10-14T06:46:01.533Z" }, + { url = "https://files.pythonhosted.org/packages/4d/42/bbd02647169e3fbed27558555653ac2578c6f17ccacf7d1956c58ef1d214/blake3-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:6a6eafc29e4f478d365a87d2f25782a521870c8514bb43734ac85ae9be71caf7", size = 215704, upload-time = "2025-10-14T06:46:02.79Z" }, + { url = "https://files.pythonhosted.org/packages/55/b8/11de9528c257f7f1633f957ccaff253b706838d22c5d2908e4735798ec01/blake3-1.0.8-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:46dc20976bd6c235959ef0246ec73420d1063c3da2839a9c87ca395cf1fd7943", size = 347771, upload-time = "2025-10-14T06:46:04.248Z" }, + { url = "https://files.pythonhosted.org/packages/50/26/f7668be55c909678b001ecacff11ad7016cd9b4e9c7cc87b5971d638c5a9/blake3-1.0.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d17eb6382634b3a5bc0c0e0454d5265b0becaeeadb6801ed25150b39a999d0cc", size = 325431, upload-time = "2025-10-14T06:46:06.136Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/e8a85fa261894bf7ce7af928ff3408aab60287ab8d58b55d13a3f700b619/blake3-1.0.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19fc6f2b7edab8acff6895fc6e38c19bd79f4c089e21153020c75dfc7397d52d", size = 370994, upload-time = "2025-10-14T06:46:07.398Z" }, + { url = "https://files.pythonhosted.org/packages/62/cd/765b76bb48b8b294fea94c9008b0d82b4cfa0fa2f3c6008d840d01a597e4/blake3-1.0.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f54cff7f15d91dc78a63a2dd02a3dccdc932946f271e2adb4130e0b4cf608ba", size = 374372, upload-time = "2025-10-14T06:46:08.698Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/32084eadbb28592bb07298f0de316d2da586c62f31500a6b1339a7e7b29b/blake3-1.0.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7e12a777f6b798eb8d06f875d6e108e3008bd658d274d8c676dcf98e0f10537", size = 447627, upload-time = "2025-10-14T06:46:10.002Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f4/3788a1d86e17425eea147e28d7195d7053565fc279236a9fd278c2ec495e/blake3-1.0.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddfc59b0176fb31168f08d5dd536e69b1f4f13b5a0f4b0c3be1003efd47f9308", size = 507536, upload-time = "2025-10-14T06:46:11.614Z" }, + { url = "https://files.pythonhosted.org/packages/fe/01/4639cba48513b94192681b4da472cdec843d3001c5344d7051ee5eaef606/blake3-1.0.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2336d5b2a801a7256da21150348f41610a6c21dae885a3acb1ebbd7333d88d8", size = 394105, upload-time = "2025-10-14T06:46:12.808Z" }, + { url = "https://files.pythonhosted.org/packages/21/ae/6e55c19c8460fada86cd1306a390a09b0c5a2e2e424f9317d2edacea439f/blake3-1.0.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4072196547484c95a5a09adbb952e9bb501949f03f9e2a85e7249ef85faaba8", size = 386928, upload-time = "2025-10-14T06:46:16.284Z" }, + { url = "https://files.pythonhosted.org/packages/ee/6c/05b7a5a907df1be53a8f19e7828986fc6b608a44119641ef9c0804fbef15/blake3-1.0.8-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0eab3318ec02f8e16fe549244791ace2ada2c259332f0c77ab22cf94dfff7130", size = 550003, upload-time = "2025-10-14T06:46:17.791Z" }, + { url = "https://files.pythonhosted.org/packages/b4/03/f0ea4adfedc1717623be6460b3710fcb725ca38082c14274369803f727e1/blake3-1.0.8-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a33b9a1fb6d1d559a8e0d04b041e99419a6bb771311c774f6ff57ed7119c70ed", size = 553857, upload-time = "2025-10-14T06:46:19.088Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6f/e5410d2e2a30c8aba8389ffc1c0061356916bf5ecd0a210344e7b69b62ab/blake3-1.0.8-cp313-cp313-win32.whl", hash = "sha256:e171b169cb7ea618e362a4dddb7a4d4c173bbc08b9ba41ea3086dd1265530d4f", size = 228315, upload-time = "2025-10-14T06:46:20.391Z" }, + { url = "https://files.pythonhosted.org/packages/79/ef/d9c297956dfecd893f29f59e7b22445aba5b47b7f6815d9ba5dcd73fcae6/blake3-1.0.8-cp313-cp313-win_amd64.whl", hash = "sha256:3168c457255b5d2a2fc356ba696996fcaff5d38284f968210d54376312107662", size = 215477, upload-time = "2025-10-14T06:46:21.542Z" }, + { url = "https://files.pythonhosted.org/packages/20/ba/eaa7723d66dd8ab762a3e85e139bb9c46167b751df6e950ad287adb8fb61/blake3-1.0.8-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4d672c24dc15ec617d212a338a4ca14b449829b6072d09c96c63b6e6b621aed", size = 347289, upload-time = "2025-10-14T06:46:22.772Z" }, + { url = "https://files.pythonhosted.org/packages/47/b3/6957f6ee27f0d5b8c4efdfda68a1298926a88c099f4dd89c711049d16526/blake3-1.0.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:1af0e5a29aa56d4fba904452ae784740997440afd477a15e583c38338e641f41", size = 324444, upload-time = "2025-10-14T06:46:24.729Z" }, + { url = "https://files.pythonhosted.org/packages/13/da/722cebca11238f3b24d3cefd2361c9c9ea47cfa0ad9288eeb4d1e0b7cf93/blake3-1.0.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef153c5860d5bf1cc71aece69b28097d2a392913eb323d6b52555c875d0439fc", size = 370441, upload-time = "2025-10-14T06:46:26.29Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d5/2f7440c8e41c0af995bad3a159e042af0f4ed1994710af5b4766ca918f65/blake3-1.0.8-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ae3689f0c7bfa6ce6ae45cab110e4c3442125c4c23b28f1f097856de26e4d1", size = 374312, upload-time = "2025-10-14T06:46:27.451Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6c/fb6a7812e60ce3e110bcbbb11f167caf3e975c589572c41e1271f35f2c41/blake3-1.0.8-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb83532f7456ddeb68dae1b36e1f7c52f9cb72852ac01159bbcb1a12b0f8be0", size = 447007, upload-time = "2025-10-14T06:46:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/13/3b/c99b43fae5047276ea9d944077c190fc1e5f22f57528b9794e21f7adedc6/blake3-1.0.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ae7754c7d96e92a70a52e07c732d594cf9924d780f49fffd3a1e9235e0f5ba7", size = 507323, upload-time = "2025-10-14T06:46:30.661Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bb/ba90eddd592f8c074a0694cb0a744b6bd76bfe67a14c2b490c8bdfca3119/blake3-1.0.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bacaae75e98dee3b7da6c5ee3b81ee21a3352dd2477d6f1d1dbfd38cdbf158a", size = 393449, upload-time = "2025-10-14T06:46:31.805Z" }, + { url = "https://files.pythonhosted.org/packages/25/ed/58a2acd0b9e14459cdaef4344db414d4a36e329b9720921b442a454dd443/blake3-1.0.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9456c829601d72852d8ba0af8dae0610f7def1d59f5942efde1e2ef93e8a8b57", size = 386844, upload-time = "2025-10-14T06:46:33.195Z" }, + { url = "https://files.pythonhosted.org/packages/4a/04/fed09845b18d90862100c8e48308261e2f663aab25d3c71a6a0bdda6618b/blake3-1.0.8-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:497ef8096ec4ac1ffba9a66152cee3992337cebf8ea434331d8fd9ce5423d227", size = 549550, upload-time = "2025-10-14T06:46:35.23Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/1859fddfabc1cc72548c2269d988819aad96d854e25eae00531517925901/blake3-1.0.8-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:511133bab85ff60ed143424ce484d08c60894ff7323f685d7a6095f43f0c85c3", size = 553805, upload-time = "2025-10-14T06:46:36.532Z" }, + { url = "https://files.pythonhosted.org/packages/c1/c7/2969352017f62378e388bb07bb2191bc9a953f818dc1cd6b9dd5c24916e1/blake3-1.0.8-cp313-cp313t-win32.whl", hash = "sha256:9c9fbdacfdeb68f7ca53bb5a7a5a593ec996eaf21155ad5b08d35e6f97e60877", size = 228068, upload-time = "2025-10-14T06:46:37.826Z" }, + { url = "https://files.pythonhosted.org/packages/d8/fc/923e25ac9cadfff1cd20038bcc0854d0f98061eb6bc78e42c43615f5982d/blake3-1.0.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3cec94ed5676821cf371e9c9d25a41b4f3ebdb5724719b31b2749653b7cc1dfa", size = 215369, upload-time = "2025-10-14T06:46:39.054Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2a/9f13ea01b03b1b4751a1cc2b6c1ef4b782e19433a59cf35b59cafb2a2696/blake3-1.0.8-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:2c33dac2c6112bc23f961a7ca305c7e34702c8177040eb98d0389d13a347b9e1", size = 347016, upload-time = "2025-10-14T06:46:40.318Z" }, + { url = "https://files.pythonhosted.org/packages/06/8e/8458c4285fbc5de76414f243e4e0fcab795d71a8b75324e14959aee699da/blake3-1.0.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c445eff665d21c3b3b44f864f849a2225b1164c08654beb23224a02f087b7ff1", size = 324496, upload-time = "2025-10-14T06:46:42.355Z" }, + { url = "https://files.pythonhosted.org/packages/49/fa/b913eb9cc4af708c03e01e6b88a8bb3a74833ba4ae4b16b87e2829198e06/blake3-1.0.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47939f04b89c5c6ff1e51e883e5efab1ea1bf01a02f4d208d216dddd63d0dd8", size = 370654, upload-time = "2025-10-14T06:46:43.907Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4f/245e0800c33b99c8f2b570d9a7199b51803694913ee4897f339648502933/blake3-1.0.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73e0b4fa25f6e3078526a592fb38fca85ef204fd02eced6731e1cdd9396552d4", size = 374693, upload-time = "2025-10-14T06:46:45.186Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a6/8cb182c8e482071dbdfcc6ec0048271fd48bcb78782d346119ff54993700/blake3-1.0.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0543c57eb9d6dac9d4bced63e9f7f7b546886ac04cec8da3c3d9c8f30cbbb7", size = 447673, upload-time = "2025-10-14T06:46:46.358Z" }, + { url = "https://files.pythonhosted.org/packages/06/b7/1cbbb5574d2a9436d1b15e7eb5b9d82e178adcaca71a97b0fddaca4bfe3a/blake3-1.0.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed972ebd553c0c25363459e9fc71a38c045d8419e365b59acd8cd791eff13981", size = 507233, upload-time = "2025-10-14T06:46:48.109Z" }, + { url = "https://files.pythonhosted.org/packages/9c/45/b55825d90af353b3e26c653bab278da9d6563afcf66736677f9397e465be/blake3-1.0.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3bafdec95dfffa3f6571e529644744e280337df15ddd9728f224ba70c5779b23", size = 393852, upload-time = "2025-10-14T06:46:49.511Z" }, + { url = "https://files.pythonhosted.org/packages/34/73/9058a1a457dd20491d1b37de53d6876eff125e1520d9b2dd7d0acbc88de2/blake3-1.0.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d78f06f3fb838b34c330e2987090376145cbe5944d8608a0c4779c779618f7b", size = 386442, upload-time = "2025-10-14T06:46:51.205Z" }, + { url = "https://files.pythonhosted.org/packages/30/6d/561d537ffc17985e276e08bf4513f1c106f1fdbef571e782604dc4e44070/blake3-1.0.8-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:dd03ff08d1b6e4fdda1cd03826f971ae8966ef6f683a8c68aa27fb21904b5aa9", size = 549929, upload-time = "2025-10-14T06:46:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/03/2f/dbe20d2c57f1a67c63be4ba310bcebc707b945c902a0bde075d2a8f5cd5c/blake3-1.0.8-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:4e02a3c499e35bf51fc15b2738aca1a76410804c877bcd914752cac4f71f052a", size = 553750, upload-time = "2025-10-14T06:46:54.194Z" }, + { url = "https://files.pythonhosted.org/packages/6b/da/c6cb712663c869b2814870c2798e57289c4268c5ac5fb12d467fce244860/blake3-1.0.8-cp314-cp314-win32.whl", hash = "sha256:a585357d5d8774aad9ffc12435de457f9e35cde55e0dc8bc43ab590a6929e59f", size = 228404, upload-time = "2025-10-14T06:46:56.807Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b6/c7dcd8bc3094bba1c4274e432f9e77a7df703532ca000eaa550bd066b870/blake3-1.0.8-cp314-cp314-win_amd64.whl", hash = "sha256:9ab5998e2abd9754819753bc2f1cf3edf82d95402bff46aeef45ed392a5468bf", size = 215460, upload-time = "2025-10-14T06:46:58.15Z" }, + { url = "https://files.pythonhosted.org/packages/75/3c/6c8afd856c353176836daa5cc33a7989e8f54569e9d53eb1c53fc8f80c34/blake3-1.0.8-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e2df12f295f95a804338bd300e8fad4a6f54fd49bd4d9c5893855a230b5188a8", size = 347482, upload-time = "2025-10-14T06:47:00.189Z" }, + { url = "https://files.pythonhosted.org/packages/6a/35/92cd5501ce8e1f5cabdc0c3ac62d69fdb13ff0b60b62abbb2b6d0a53a790/blake3-1.0.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:63379be58438878eeb76ebe4f0efbeaabf42b79f2cff23b6126b7991588ced67", size = 324376, upload-time = "2025-10-14T06:47:01.413Z" }, + { url = "https://files.pythonhosted.org/packages/11/33/503b37220a3e2e31917ef13722efd00055af51c5e88ae30974c733d7ece6/blake3-1.0.8-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88d527c247f9609dc1d45a08fd243e39f0d5300d54c57e048de24d4fa9240ebb", size = 370220, upload-time = "2025-10-14T06:47:02.573Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/fe817843adf59516c04d44387bd643b422a3b0400ea95c6ede6a49920737/blake3-1.0.8-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506a47897a11ebe8f3cdeb52f1365d6a2f83959e98ccb0c830f8f73277d4d358", size = 373454, upload-time = "2025-10-14T06:47:03.784Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/90a2a623575373dfc9b683f1bad1bf017feafa5a6d65d94fb09543050740/blake3-1.0.8-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5122a61b3b004bbbd979bdf83a3aaab432da3e2a842d7ddf1c273f2503b4884", size = 447102, upload-time = "2025-10-14T06:47:04.958Z" }, + { url = "https://files.pythonhosted.org/packages/93/ff/4e8ce314f60115c4c657b1fdbe9225b991da4f5bcc5d1c1f1d151e2f39d6/blake3-1.0.8-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0171e85d56dec1219abdae5f49a0ed12cb3f86a454c29160a64fd8a8166bba37", size = 506791, upload-time = "2025-10-14T06:47:06.82Z" }, + { url = "https://files.pythonhosted.org/packages/44/88/2963a1f18aab52bdcf35379b2b48c34bbc462320c37e76960636b8602c36/blake3-1.0.8-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:003f61e8c41dd9931edddf1cc6a1bb680fb2ac0ad15493ef4a1df9adc59ce9df", size = 393717, upload-time = "2025-10-14T06:47:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/45/d1/a848ed8e8d4e236b9b16381768c9ae99d92890c24886bb4505aa9c3d2033/blake3-1.0.8-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c3151955efb09ba58cd3e1263521e15e9e3866a40d6bd3556d86fc968e8f95", size = 386150, upload-time = "2025-10-14T06:47:10.363Z" }, + { url = "https://files.pythonhosted.org/packages/96/09/e3eb5d60f97c01de23d9f434e6e1fc117efb466eaa1f6ddbbbcb62580d6e/blake3-1.0.8-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:5eb25bca3cee2e0dd746a214784fb36be6a43640c01c55b6b4e26196e72d076c", size = 549120, upload-time = "2025-10-14T06:47:11.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/ad/3d9661c710febb8957dd685fdb3e5a861aa0ac918eda3031365ce45789e2/blake3-1.0.8-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ab4e1dea4fa857944944db78e8f20d99ee2e16b2dea5a14f514fb0607753ac83", size = 553264, upload-time = "2025-10-14T06:47:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/11/55/e332a5b49edf377d0690e95951cca21a00c568f6e37315f9749efee52617/blake3-1.0.8-cp314-cp314t-win32.whl", hash = "sha256:67f1bc11bf59464ef092488c707b13dd4e872db36e25c453dfb6e0c7498df9f1", size = 228116, upload-time = "2025-10-14T06:47:14.516Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5c/dbd00727a3dd165d7e0e8af40e630cd7e45d77b525a3218afaff8a87358e/blake3-1.0.8-cp314-cp314t-win_amd64.whl", hash = "sha256:421b99cdf1ff2d1bf703bc56c454f4b286fce68454dd8711abbcb5a0df90c19a", size = 215133, upload-time = "2025-10-14T06:47:16.069Z" }, +] + +[[package]] +name = "brotli" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/ee/b0a11ab2315c69bb9b45a2aaed022499c9c24a205c3a49c3513b541a7967/brotli-1.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84", size = 861543, upload-time = "2025-11-05T18:38:24.183Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2f/29c1459513cd35828e25531ebfcbf3e92a5e49f560b1777a9af7203eb46e/brotli-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b", size = 444288, upload-time = "2025-11-05T18:38:25.139Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/feba03130d5fceadfa3a1bb102cb14650798c848b1df2a808356f939bb16/brotli-1.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d", size = 1528071, upload-time = "2025-11-05T18:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/2b/38/f3abb554eee089bd15471057ba85f47e53a44a462cfce265d9bf7088eb09/brotli-1.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca", size = 1626913, upload-time = "2025-11-05T18:38:27.284Z" }, + { url = "https://files.pythonhosted.org/packages/03/a7/03aa61fbc3c5cbf99b44d158665f9b0dd3d8059be16c460208d9e385c837/brotli-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f", size = 1419762, upload-time = "2025-11-05T18:38:28.295Z" }, + { url = "https://files.pythonhosted.org/packages/21/1b/0374a89ee27d152a5069c356c96b93afd1b94eae83f1e004b57eb6ce2f10/brotli-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28", size = 1484494, upload-time = "2025-11-05T18:38:29.29Z" }, + { url = "https://files.pythonhosted.org/packages/cf/57/69d4fe84a67aef4f524dcd075c6eee868d7850e85bf01d778a857d8dbe0a/brotli-1.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7", size = 1593302, upload-time = "2025-11-05T18:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3b/39e13ce78a8e9a621c5df3aeb5fd181fcc8caba8c48a194cd629771f6828/brotli-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036", size = 1487913, upload-time = "2025-11-05T18:38:31.618Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/4d00cb9bd76a6357a66fcd54b4b6d70288385584063f4b07884c1e7286ac/brotli-1.2.0-cp312-cp312-win32.whl", hash = "sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161", size = 334362, upload-time = "2025-11-05T18:38:32.939Z" }, + { url = "https://files.pythonhosted.org/packages/1c/4e/bc1dcac9498859d5e353c9b153627a3752868a9d5f05ce8dedd81a2354ab/brotli-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44", size = 369115, upload-time = "2025-11-05T18:38:33.765Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d4/4ad5432ac98c73096159d9ce7ffeb82d151c2ac84adcc6168e476bb54674/brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab", size = 861523, upload-time = "2025-11-05T18:38:34.67Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/9cc5bd03ee68a85dc4bc89114f7067c056a3c14b3d95f171918c088bf88d/brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c", size = 444289, upload-time = "2025-11-05T18:38:35.6Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b6/fe84227c56a865d16a6614e2c4722864b380cb14b13f3e6bef441e73a85a/brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f", size = 1528076, upload-time = "2025-11-05T18:38:36.639Z" }, + { url = "https://files.pythonhosted.org/packages/55/de/de4ae0aaca06c790371cf6e7ee93a024f6b4bb0568727da8c3de112e726c/brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6", size = 1626880, upload-time = "2025-11-05T18:38:37.623Z" }, + { url = "https://files.pythonhosted.org/packages/5f/16/a1b22cbea436642e071adcaf8d4b350a2ad02f5e0ad0da879a1be16188a0/brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c", size = 1419737, upload-time = "2025-11-05T18:38:38.729Z" }, + { url = "https://files.pythonhosted.org/packages/46/63/c968a97cbb3bdbf7f974ef5a6ab467a2879b82afbc5ffb65b8acbb744f95/brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48", size = 1484440, upload-time = "2025-11-05T18:38:39.916Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/102c67ea5c9fc171f423e8399e585dabea29b5bc79b05572891e70013cdd/brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18", size = 1593313, upload-time = "2025-11-05T18:38:41.24Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4a/9526d14fa6b87bc827ba1755a8440e214ff90de03095cacd78a64abe2b7d/brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5", size = 1487945, upload-time = "2025-11-05T18:38:42.277Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e8/3fe1ffed70cbef83c5236166acaed7bb9c766509b157854c80e2f766b38c/brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a", size = 334368, upload-time = "2025-11-05T18:38:43.345Z" }, + { url = "https://files.pythonhosted.org/packages/ff/91/e739587be970a113b37b821eae8097aac5a48e5f0eca438c22e4c7dd8648/brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8", size = 369116, upload-time = "2025-11-05T18:38:44.609Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/298c2ddf786bb7347a1cd71d63a347a79e5712a7c0cba9e3c3458ebd976f/brotli-1.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21", size = 863080, upload-time = "2025-11-05T18:38:45.503Z" }, + { url = "https://files.pythonhosted.org/packages/84/0c/aac98e286ba66868b2b3b50338ffbd85a35c7122e9531a73a37a29763d38/brotli-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac", size = 445453, upload-time = "2025-11-05T18:38:46.433Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f1/0ca1f3f99ae300372635ab3fe2f7a79fa335fee3d874fa7f9e68575e0e62/brotli-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e", size = 1528168, upload-time = "2025-11-05T18:38:47.371Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a6/2ebfc8f766d46df8d3e65b880a2e220732395e6d7dc312c1e1244b0f074a/brotli-1.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7", size = 1627098, upload-time = "2025-11-05T18:38:48.385Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/0976d5b097ff8a22163b10617f76b2557f15f0f39d6a0fe1f02b1a53e92b/brotli-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63", size = 1419861, upload-time = "2025-11-05T18:38:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/d76df7176a2ce7616ff94c1fb72d307c9a30d2189fe877f3dd99af00ea5a/brotli-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b", size = 1484594, upload-time = "2025-11-05T18:38:50.655Z" }, + { url = "https://files.pythonhosted.org/packages/d3/93/14cf0b1216f43df5609f5b272050b0abd219e0b54ea80b47cef9867b45e7/brotli-1.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361", size = 1593455, upload-time = "2025-11-05T18:38:51.624Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/3183c9e41ca755713bdf2cc1d0810df742c09484e2e1ddd693bee53877c1/brotli-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888", size = 1488164, upload-time = "2025-11-05T18:38:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/64/6a/0c78d8f3a582859236482fd9fa86a65a60328a00983006bcf6d83b7b2253/brotli-1.2.0-cp314-cp314-win32.whl", hash = "sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d", size = 339280, upload-time = "2025-11-05T18:38:54.02Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" }, +] + +[[package]] +name = "cachetools" +version = "7.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/8b/0d3945a13955303b81272f759a0331e54c5c793da455e6f5706b89d2639c/cachetools-7.1.4.tar.gz", hash = "sha256:437f55a4e0c1b01a4f3077cc470e6991d47430970e36fbcb77e2be0df4fc1cd6", size = 40085, upload-time = "2026-05-21T22:40:43.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/7b/1fc1c09cc0756cf25861a3be10565915953876da48bb228fb9a672b20a42/cachetools-7.1.4-py3-none-any.whl", hash = "sha256:323dc4127934744db5b54eb4924482d7edafbf9554e820d1531c2e08c0e4ef54", size = 16761, upload-time = "2026-05-21T22:40:41.845Z" }, +] + +[[package]] +name = "caio" +version = "0.9.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/88/b8527e1b00c1811db339a1df8bd1ae49d146fcea9d6a5c40e3a80aaeb38d/caio-0.9.25.tar.gz", hash = "sha256:16498e7f81d1d0f5a4c0ad3f2540e65fe25691376e0a5bd367f558067113ed10", size = 26781, upload-time = "2025-12-26T15:21:36.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/25/79c98ebe12df31548ba4eaf44db11b7cad6b3e7b4203718335620939083c/caio-0.9.25-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb7ff95af4c31ad3f03179149aab61097a71fd85e05f89b4786de0359dffd044", size = 36983, upload-time = "2025-12-26T15:21:36.075Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2b/21288691f16d479945968a0a4f2856818c1c5be56881d51d4dac9b255d26/caio-0.9.25-cp312-cp312-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:97084e4e30dfa598449d874c4d8e0c8d5ea17d2f752ef5e48e150ff9d240cd64", size = 82012, upload-time = "2025-12-26T15:22:20.983Z" }, + { url = "https://files.pythonhosted.org/packages/03/c4/8a1b580875303500a9c12b9e0af58cb82e47f5bcf888c2457742a138273c/caio-0.9.25-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:4fa69eba47e0f041b9d4f336e2ad40740681c43e686b18b191b6c5f4c5544bfb", size = 81502, upload-time = "2026-03-04T22:08:22.381Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/0fe770b8ffc8362c48134d1592d653a81a3d8748d764bec33864db36319d/caio-0.9.25-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:6bebf6f079f1341d19f7386db9b8b1f07e8cc15ae13bfdaff573371ba0575d69", size = 80200, upload-time = "2026-03-04T22:08:23.382Z" }, + { url = "https://files.pythonhosted.org/packages/31/57/5e6ff127e6f62c9f15d989560435c642144aa4210882f9494204bc892305/caio-0.9.25-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d6c2a3411af97762a2b03840c3cec2f7f728921ff8adda53d7ea2315a8563451", size = 36979, upload-time = "2025-12-26T15:21:35.484Z" }, + { url = "https://files.pythonhosted.org/packages/a3/9f/f21af50e72117eb528c422d4276cbac11fb941b1b812b182e0a9c70d19c5/caio-0.9.25-cp313-cp313-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0998210a4d5cd5cb565b32ccfe4e53d67303f868a76f212e002a8554692870e6", size = 81900, upload-time = "2025-12-26T15:22:21.919Z" }, + { url = "https://files.pythonhosted.org/packages/9c/12/c39ae2a4037cb10ad5eb3578eb4d5f8c1a2575c62bba675f3406b7ef0824/caio-0.9.25-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:1a177d4777141b96f175fe2c37a3d96dec7911ed9ad5f02bac38aaa1c936611f", size = 81523, upload-time = "2026-03-04T22:08:25.187Z" }, + { url = "https://files.pythonhosted.org/packages/22/59/f8f2e950eb4f1a5a3883e198dca514b9d475415cb6cd7b78b9213a0dd45a/caio-0.9.25-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:9ed3cfb28c0e99fec5e208c934e5c157d0866aa9c32aa4dc5e9b6034af6286b7", size = 80243, upload-time = "2026-03-04T22:08:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/69/ca/a08fdc7efdcc24e6a6131a93c85be1f204d41c58f474c42b0670af8c016b/caio-0.9.25-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fab6078b9348e883c80a5e14b382e6ad6aabbc4429ca034e76e730cf464269db", size = 36978, upload-time = "2025-12-26T15:21:41.055Z" }, + { url = "https://files.pythonhosted.org/packages/5e/6c/d4d24f65e690213c097174d26eda6831f45f4734d9d036d81790a27e7b78/caio-0.9.25-cp314-cp314-manylinux2010_x86_64.manylinux2014_x86_64.manylinux_2_12_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:44a6b58e52d488c75cfaa5ecaa404b2b41cc965e6c417e03251e868ecd5b6d77", size = 81832, upload-time = "2025-12-26T15:22:22.757Z" }, + { url = "https://files.pythonhosted.org/packages/87/a4/e534cf7d2d0e8d880e25dd61e8d921ffcfe15bd696734589826f5a2df727/caio-0.9.25-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:628a630eb7fb22381dd8e3c8ab7f59e854b9c806639811fc3f4310c6bd711d79", size = 81565, upload-time = "2026-03-04T22:08:27.483Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ed/bf81aeac1d290017e5e5ac3e880fd56ee15e50a6d0353986799d1bc5cfd5/caio-0.9.25-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:0ba16aa605ccb174665357fc729cf500679c2d94d5f1458a6f0d5ca48f2060a7", size = 80071, upload-time = "2026-03-04T22:08:28.751Z" }, + { url = "https://files.pythonhosted.org/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087, upload-time = "2025-12-26T15:22:00.221Z" }, +] + +[[package]] +name = "cbor2" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/db/810437bcfe13cf5e09b68bad1ce57c8fa04ca9272c68946bbf2f4fa522c8/cbor2-6.1.1.tar.gz", hash = "sha256:6f0644869e0fdcd6f3874330b8f1cebd009f33191de43acf609dc2409cd362c4", size = 86297, upload-time = "2026-05-14T10:57:42.231Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/16/ac4710211e506a522bfe522dc02d676f308cff24c512b375b10e1cff62ed/cbor2-6.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1f72e5f7e42a92f5ad2486dd14431bd09f966d167fc9e61cecef6740acf1b451", size = 410055, upload-time = "2026-05-14T10:56:54.133Z" }, + { url = "https://files.pythonhosted.org/packages/13/3a/ae0df2f8e4f8fac9212a3a9684a6213b6ba3190cd7762d78e5bd5043dddb/cbor2-6.1.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a409b0b6de923f68f5e35287f25ec654fc68135991e41ae9a1c500ddd982c1fb", size = 453919, upload-time = "2026-05-14T10:56:55.468Z" }, + { url = "https://files.pythonhosted.org/packages/87/4c/f5b3feb35e942998f60545199ff9c4c80d552a8b783d07f7ff70e78e8b1f/cbor2-6.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:911b34263f39300dd8ec6b78f247b257caba0bbcd278bd2421a54d45595ff602", size = 467302, upload-time = "2026-05-14T10:56:56.76Z" }, + { url = "https://files.pythonhosted.org/packages/17/6d/a0472d99d9a38728498c9bcb4c65687383a948b0152e0bd7a20c1a87c949/cbor2-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:596418d033cff6eb0de9cb4ae63dd91c80e68d4ed01e1d0c61ad51709acc8ed2", size = 521305, upload-time = "2026-05-14T10:56:58.484Z" }, + { url = "https://files.pythonhosted.org/packages/c0/28/1d8cdb754def050e0d0674a556540d4a26bab0d7cfc3e11df14f2e4a2830/cbor2-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce0e9a33d7ee2c8f47ae216be68a3a0a4d6d9832594a69e34be070cf6d13a9d8", size = 534365, upload-time = "2026-05-14T10:56:59.85Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fe/eaac5df152999aad4f3b4c4a25d0268b422dfebdbec28ebde8d3668604c5/cbor2-6.1.1-cp312-cp312-win32.whl", hash = "sha256:835f789f526ca7e729a8957da5ff6f33dfbda6c0b068695d01872fd6e35bbec2", size = 282520, upload-time = "2026-05-14T10:57:01.177Z" }, + { url = "https://files.pythonhosted.org/packages/c4/e4/f1e480dbe8c11f5edf86c123adc25cfaf2eea1e80740da99e9cef735ae8f/cbor2-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:63ab065ae26e48d39fc6f4d7f44dd0780afdb91a70ffb8f33e281f54ee35ad14", size = 300677, upload-time = "2026-05-14T10:57:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f8/2534292515d113b1fb319e0bdc2ee508be9d9d2ce2389dbee00a66dfb97e/cbor2-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:199cbe1fa0326ec06f1d986bdfe488b3cafd2b1b5367a81c8f53c8364cab4803", size = 288811, upload-time = "2026-05-14T10:57:04.519Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ec/30a52d7f6844cefd37601311a226d091268564a47b0dac56bc0469573681/cbor2-6.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f027e077345ba7d1a88cbed9168196e77f5ce8e8c816305bb1c7a2e4894bddf", size = 409070, upload-time = "2026-05-14T10:57:05.843Z" }, + { url = "https://files.pythonhosted.org/packages/b7/a5/653193249a64ca46def52798e8f10ddbc918f11818a977b2aa7248062520/cbor2-6.1.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:559025ad8e1f9f5d019a40dc8f14f43c111c11207b4dde852e943a3002b43ec0", size = 453218, upload-time = "2026-05-14T10:57:07.6Z" }, + { url = "https://files.pythonhosted.org/packages/9f/79/bdcb9d43ed537abaa89e662d6340244207ec85b6e66e3bd7f40856c3a5d4/cbor2-6.1.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:a6690f7df210386866e120475183132df98f77bf6df624097f66e3214e775084", size = 466244, upload-time = "2026-05-14T10:57:09.297Z" }, + { url = "https://files.pythonhosted.org/packages/9c/44/fe0543996d53538c074f8ee18f7391b5458c528b1717740d750a9e472e1d/cbor2-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f4898b5463a567775a05310407dbea5b4a8d7ae8e81337ae9084f5fe226938ff", size = 520804, upload-time = "2026-05-14T10:57:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/cd/83/577bbafef3bc887d654a73f3f4ab11e1bd5320abd9108bfc51fbea1498a8/cbor2-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf3ef1fae6f14081a15f178e933ab846d3181f059ee4090975518b71f58bb09f", size = 533598, upload-time = "2026-05-14T10:57:12.098Z" }, + { url = "https://files.pythonhosted.org/packages/57/32/c1c9f435b109ded86ef2e90ff73b95624c84c6edf01489941363a6069725/cbor2-6.1.1-cp313-cp313-win32.whl", hash = "sha256:4642780d27c0b411f4669fcb82e0d7a6b93a0c41c03a0c51296fd6f6858f63fa", size = 281738, upload-time = "2026-05-14T10:57:13.614Z" }, + { url = "https://files.pythonhosted.org/packages/4d/39/9232731f161b2dfe2dc28b06bbacfc2b6a85f1255bf58ebc578ae760ef38/cbor2-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:616bc0538095860fe5607cc06d7b2de3e261a6caccd01ff3f1d4a4a9ad29adbf", size = 300018, upload-time = "2026-05-14T10:57:15.021Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c2/67f2e3a83acfcecad947784bb1590d1978662b5472fcbf7d73e219813456/cbor2-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:7b193d2d024bb5d037e613272f5e436d53f02301101f0ce3916117688643181f", size = 287823, upload-time = "2026-05-14T10:57:16.525Z" }, + { url = "https://files.pythonhosted.org/packages/aa/74/d2d6e0e3da305a625d710a932080bf70f390c867dce73bd35ca6cd5a8d10/cbor2-6.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:86c65976e9e69154700ea5a447013f37ff8cb76431adf9df3ebbabe341b68b06", size = 407425, upload-time = "2026-05-14T10:57:17.814Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7d/08644318380306e0809ecc4756e67fb684b5e78a938ca9ff1c8c7f57fe73/cbor2-6.1.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:350beaac7a6049fe0a48309d7acd24611ab1176b4db1515f7fbcad20f5c09821", size = 453010, upload-time = "2026-05-14T10:57:19.593Z" }, + { url = "https://files.pythonhosted.org/packages/81/ff/43ef5f16a1a97ef4575c407d077d9355c01dfc54b1b1b8c5329b793c436b/cbor2-6.1.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:74bf0c3f48d215d49a99eb253fef6c00c19033339da22da4c29b53fe854093b8", size = 465110, upload-time = "2026-05-14T10:57:20.981Z" }, + { url = "https://files.pythonhosted.org/packages/c8/61/3069cee66bc4bedb95dce49b5e90d07e6c1ddf712435facf84ce0353da4a/cbor2-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a731277d123cee9c87e649077376f694892e4a2c3b0b1cb97132205c620947d8", size = 520269, upload-time = "2026-05-14T10:57:22.514Z" }, + { url = "https://files.pythonhosted.org/packages/f0/70/4b2ac02e0aa09419c13c434ce535cf508f08d5c411c6912d760c480ed8e6/cbor2-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:16e6df5a4971c2006805669be472a43bb382d0f3464c2236634b4e93095d7dd6", size = 532515, upload-time = "2026-05-14T10:57:24.289Z" }, + { url = "https://files.pythonhosted.org/packages/73/94/ab4ad4fd5929c1df56899c1135cc6957239a74a5b418e760502c9aadfb17/cbor2-6.1.1-cp314-cp314-win32.whl", hash = "sha256:0d0831b449567ee27afa25ff2756ac8719f11491f700396edb1dc1647ece7111", size = 285433, upload-time = "2026-05-14T10:57:25.665Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ed/995a3830ce4429be1ffeb57d2f11b2f06987573c04a4ea4112bd5d7de643/cbor2-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:91dac40fc0b8e0592a3e8d766377af3186e2736448c684465cca8606486e58ae", size = 308923, upload-time = "2026-05-14T10:57:27.019Z" }, + { url = "https://files.pythonhosted.org/packages/ea/88/1797af54eca15bca2d963cd2d3a7337758961a31fd03438f2e82ec94ea87/cbor2-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:be5ccd594ea6f1998cd83afb53b47e383e5efd7661a316a528216412109221c7", size = 299687, upload-time = "2026-05-14T10:57:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/53/dc/ecc0797db8b627f889389d8ea8a4af389bdff7500685e56969a6c4449c01/cbor2-6.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:47d2616d30212bd3db8c2897b453176401569e0e4ec3434b770e9652604d74c5", size = 403186, upload-time = "2026-05-14T10:57:30.111Z" }, + { url = "https://files.pythonhosted.org/packages/c6/28/780af53231e1a6afc36f2b922ff587a9e1a25df7756628101a6070a9312f/cbor2-6.1.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fd9d300ad983b860fbfb0ab148ddd3a379be25430bb141ad41344adc1c0792c1", size = 446311, upload-time = "2026-05-14T10:57:31.507Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5d/cc298ed16745995cf21caeec52213d157be8d5bfb405ee8ed420ffb5e038/cbor2-6.1.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:b8594563ccfd56f2bb56cdd8445f7a1f00d3065d84ea06f8e361da765abee08f", size = 459640, upload-time = "2026-05-14T10:57:32.967Z" }, + { url = "https://files.pythonhosted.org/packages/f1/37/e4d95459d48e8a739c086249884b27458541df5a7fc149debdb0e0c7becb/cbor2-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8df2a530b45c7769ed43c02e3f7c9841ed4990887e1c29858b08363a35067bf5", size = 511667, upload-time = "2026-05-14T10:57:34.465Z" }, + { url = "https://files.pythonhosted.org/packages/40/e8/32e529bd938c71456d38d7c6a62d0d75399e720553d6514a467fee9b004d/cbor2-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d63181b5b213ab72eed01e62bfa4c994fe7de68433d12548d54156411ba0aac4", size = 527195, upload-time = "2026-05-14T10:57:36.09Z" }, + { url = "https://files.pythonhosted.org/packages/be/96/42275a7d34baa8457a686c5e5a3bf5240e753595a6bd79c2c419347a2083/cbor2-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:cba9a9ebc031267b76c2bdfd4a5a491874c27339d6ec9d0895fc4fde8f519565", size = 279851, upload-time = "2026-05-14T10:57:37.443Z" }, + { url = "https://files.pythonhosted.org/packages/e3/97/09053af3e4825aa3b83b1ec2306c9228efe665fbfb90229e441b9c1b3cd5/cbor2-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:81af6e3a031191b483ca42b16d152627c6a9dc61c1fbef270403820ab587fc86", size = 302537, upload-time = "2026-05-14T10:57:39.143Z" }, + { url = "https://files.pythonhosted.org/packages/4f/29/e257a381d494615348c7266fc173a36edce142533a5befe3c0967fd45ab4/cbor2-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:f0bc04543c562bd0b35fc79a29528017fe63104757c1b421d8c1ddfbe6761eca", size = 290270, upload-time = "2026-05-14T10:57:40.597Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, +] + +[[package]] +name = "cloudflare" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/41/1f4245b1e5f48abf9f50a38023309f9ac697b0b7757a5ab326f38a1d87b4/cloudflare-5.2.0.tar.gz", hash = "sha256:dcf5cb4b54f543b000a68095297bcc16a9c618b98c09747d7b78e59e3d865292", size = 2882432, upload-time = "2026-05-21T22:31:39.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/47/147354a1ae59a9d00e57f8349a406242765499f5bb2ac35d3cf583cda999/cloudflare-5.2.0-py3-none-any.whl", hash = "sha256:9268c67e76eef84734680d6c322d115ab6613ec9096f9b8d4389e858c4e12549", size = 6136517, upload-time = "2026-05-21T22:31:37.171Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "compressed-tensors" +version = "0.15.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "loguru" }, + { name = "pydantic" }, + { name = "torch" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/1b/c3c4a98ec5f2727656336f07a0c35862195c310d8eb0b2fa5b4be6848680/compressed_tensors-0.15.0.1.tar.gz", hash = "sha256:a8e93054e8a5ec49c980b09ed36c4c1249b4a8ee167920a8e461c4da26e78d99", size = 229412, upload-time = "2026-04-10T14:23:54.708Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/52/93833dc1610e017ac5b7dcd59b8304d8ef67d1114c2d124e728a2cbbea12/compressed_tensors-0.15.0.1-py3-none-any.whl", hash = "sha256:e1b1f322e82e475715e242bad46925a304ea8e5c98b5055a15b8eb22fb6bfea9", size = 194260, upload-time = "2026-04-10T14:23:53.098Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "13.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/67/5e7dba1ba576dd73da5dee894ca076ca5e959450dfff66d6d510a255d1f7/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7855c4868aabc0cfae28abbe83d56734bdfbd08f08fc234ac1912a12858bf49", size = 6025351, upload-time = "2026-05-29T23:11:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/6d2e9047d1fb243dbaa364b01e0297534b9ed7fd27dba1c9f361519cf69b/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e32d08f71ebcdf00f0f41eab2eb37e8da94c8ed411cc9f7f7a019ce6b34abe3a", size = 6657965, upload-time = "2026-05-29T23:11:52.227Z" }, + { url = "https://files.pythonhosted.org/packages/7c/95/872a0392122f1fb43fcb06869790ef3171f37beee9f7db8f441739113570/cuda_bindings-13.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:b134dd8c5c66ae4c4ad814f7aee88fd215353c077010cbc47e3b55ed35ec9eff", size = 5875099, upload-time = "2026-05-29T23:11:54.635Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6e/2394f8163360f8391f8f1b7e72d300a82724edb81a7b7084c799fbd4c91f/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9efb21c1ee64981e184b9e0ba5eb3179e5ba3d4b51665a6cb52b8ef3d01a7cbf", size = 5920504, upload-time = "2026-05-29T23:11:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/34/c2/ef9b6a63f7dc432712a462c816662e662e00d38caa9b861c8c2588195d03/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2732904099e0a4d4db774a5fc6d91ee95fae065b4d2ecabb4968c5fe2406c9d7", size = 6476660, upload-time = "2026-05-29T23:11:59.188Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2f/6a0dd496550c6fafbf6aeb1bf40242eeabb2fd138a43892aabb4be8224c2/cuda_bindings-13.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:18c8c167c8907b8f02531ca810534315c458dabef31f7965095619bf647b9202", size = 5830027, upload-time = "2026-05-29T23:12:01.205Z" }, + { url = "https://files.pythonhosted.org/packages/b1/81/bff68ce829999c1e4209c761bbf903b1c06ec570416ddb25020864ad5907/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ab2f74ed65bfef4163ba07a8db16f1085e0729291db12a2423aff84ee8278b8", size = 6013639, upload-time = "2026-05-29T23:12:03.509Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e0/c8a1f0c8f9ffdea4f5fe6dbab89b326cef4d85caf489dad39e209da89416/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd4c814d311ec08c981f6dded1dbe7d4b371067ee4f6c14cccec4bde9590f80", size = 6534419, upload-time = "2026-05-29T23:12:05.633Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/2734be44dbc80ac082ec23a86b41c8294992dcb90033645ed1bc50aafe4c/cuda_bindings-13.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:8de12ef60bf40756852cb62bbb40460609269f6ece522903d1cc93d73a3ececb", size = 5961055, upload-time = "2026-05-29T23:12:07.971Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/83b1f563925b290f2d11a01a77a84013ba56052fe3653a5bef3ccfbb43d6/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3c772dfff49681541d59630c90f858e173ac926b9c593a2b7123f2a1043cc76", size = 5809771, upload-time = "2026-05-29T23:12:10.422Z" }, + { url = "https://files.pythonhosted.org/packages/12/20/e79b4bfe98f075195afb6343d41c498f9dbd2d161d7021d4d28bceb83581/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36febb7c1079d68a981dbbd8d5a67235b399802b82075c9388624719607e52b9", size = 6358584, upload-time = "2026-05-29T23:12:12.767Z" }, + { url = "https://files.pythonhosted.org/packages/27/2a/b59bcac016ab9985d6b48a5d05b0d698461a159ca03ee11c4abd54da2ac4/cuda_bindings-13.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61120b5e4f4a63f67efd7e7396914cb9ef871bb1f0021e990fb70277be240a4d", size = 6740329, upload-time = "2026-05-29T23:12:15.153Z" }, +] + +[[package]] +name = "cuda-core" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/a0/1daeae599cadd612689dbbf70d7da1c01883964fc2fbc7386f3c630a68cf/cuda_core-1.0.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6816dc020aee6103d8071bc02d8e4e1d91f2b49596f666896d608d92224d79d1", size = 4789856, upload-time = "2026-05-12T20:11:30.862Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4d/603557ab3cb171cc2a61d3678a39cb4dae3fd21275078bfbd1c0b0b5230b/cuda_core-1.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be7b65311bf78964b7905adbf3c0f8f717d432f2854dc45169277729bf60f1e2", size = 5106023, upload-time = "2026-05-12T20:11:33.509Z" }, + { url = "https://files.pythonhosted.org/packages/c2/1a/ae079963c9df7f4274227eb63cf8f6083a532a6443adb340d951fd21c626/cuda_core-1.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a5c1aa3b738a7599ea289498d038fe625d259fd7ab795394541eee58a8e29bc", size = 4663076, upload-time = "2026-05-12T20:11:35.784Z" }, + { url = "https://files.pythonhosted.org/packages/57/f9/a6676b1fa555fad5748a945f4b530b51b898b4771a1e5d9f3520d3f415ea/cuda_core-1.0.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c427e5025096d96fcd5092fdc85d5d5e4ac3dea007914e90472ed52f27220446", size = 4749800, upload-time = "2026-05-12T20:11:38.012Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9d/4534a9564a812ee95b43db7324f9b25cbffda001bb348bb5b3f90dad50b9/cuda_core-1.0.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b392178202c652368883dbe3773cee14f3e1ed6b8bf45d1a1bcdd37c73604e06", size = 5078597, upload-time = "2026-05-12T20:11:40.836Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7c/2f68b0bdeb7dd36204f752468254d6b4487c6d82e9e442cfbe815a656eac/cuda_core-1.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:b99e3ca9bf3bd2c7d3028e5dc541b00e432e21373816889cdf2722b675bd9be8", size = 4647545, upload-time = "2026-05-12T20:11:43.494Z" }, + { url = "https://files.pythonhosted.org/packages/c2/45/55b07d643c87f1234b3cbc9d8383c0962b368ba1d6686a1919d6f6001af4/cuda_core-1.0.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9b0f115a68f2f84c0f6d9b7e863a29517f6dfe5f7b7d07d1d9da8904754e9a2", size = 4816184, upload-time = "2026-05-12T20:11:46.114Z" }, + { url = "https://files.pythonhosted.org/packages/51/08/1aeffc9a529a7f94c9cee9bfd3a991743398b5f90aab30f06f2a4bc8205e/cuda_core-1.0.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:af2db9e50e81d73e4f0b72ad279d0a9c789372393938fe75c17236b9ed974d7d", size = 5104496, upload-time = "2026-05-12T20:11:48.518Z" }, + { url = "https://files.pythonhosted.org/packages/66/66/965330a44bcfa548793cf13244083528a597da2a18ff42d00fe8ef91ba03/cuda_core-1.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:29783ba03d36960b6612b15ff97123e88cfadfb7b1884f3581839f6bfba4b29f", size = 4765708, upload-time = "2026-05-12T20:11:51.107Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ab/db09228d5a8c124a93514726d2e18f31824f66d3a6769ee4e51721dd64cf/cuda_core-1.0.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4410bf1ef15c2ec23dccc302da76893c9354b530dee422e3277b116231bf5fe1", size = 4996094, upload-time = "2026-05-12T20:11:53.575Z" }, + { url = "https://files.pythonhosted.org/packages/29/e7/8ced56d6c6fa32b7385a8dccefd1424e3c1201bfee3385d9710609a43d16/cuda_core-1.0.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0f1324486bb90be6bde28bd0afbaf78a38827948d37f93f90318a01da8a3f8c", size = 5212461, upload-time = "2026-05-12T20:11:56.238Z" }, + { url = "https://files.pythonhosted.org/packages/da/06/36236c44ed4025a6cbf5b6450364fc29e0f3d35ef4becb13b168fcd271f4/cuda_core-1.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:180bc808166483b0d6658a7c0d3f1312083b220f301de49476c1bc459cc46cf8", size = 5551965, upload-time = "2026-05-12T20:11:58.84Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c8/26f2e4aae92f11522a96043892ba39a90eac610d5242523aa863212bc1c7/cuda_pathfinder-1.5.5-py3-none-any.whl", hash = "sha256:0228c023f95d1480f143ef5c8922d27a2ab052087a942e81dc289c9eb8f91689", size = 51671, upload-time = "2026-05-27T01:21:25.413Z" }, +] + +[[package]] +name = "cuda-python" +version = "13.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings" }, + { name = "cuda-core" }, + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/31/7ff3f7768eded7535c621abc2fecb9d181a34ea4cae3afe682feb796f242/cuda_python-13.3.1-py3-none-any.whl", hash = "sha256:280b014139ab447b6dd70a377db1596f310d6e887d9d342e6651b919ec145fb3", size = 8295, upload-time = "2026-05-29T23:28:47.012Z" }, +] + +[[package]] +name = "cuda-tile" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/93/64ef40d3982dcda7a97ebfa3e3bb9045b573d4eb3877fa5d1fa3cd2541d3/cuda_tile-1.4.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:9e358a85a153820aa0a51d0e09346d884a3c14b88c0313d20d0fb9f53952abae", size = 280953, upload-time = "2026-05-27T17:46:53.03Z" }, + { url = "https://files.pythonhosted.org/packages/d7/9a/7fbdbdb30c375f80818941165adfc4f1dc6cebaf937c6a9081a02d5871f0/cuda_tile-1.4.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:1d9d99b6fa57366af3f8707ac4fd91411275af2ee736996a60620240fcf92070", size = 282503, upload-time = "2026-05-27T17:45:05.543Z" }, + { url = "https://files.pythonhosted.org/packages/6f/bb/4152dc08a8de5bcdc4b9d80b6917216289526f6e786b09ee80d4df27bcfb/cuda_tile-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:616f13cbc7af6caa7b92430b85ba0a429d1f96ca9e7e04a29d89114cfe859663", size = 269813, upload-time = "2026-05-27T17:46:20.583Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ad/42f0655e6aee5c59015634b46d7f13bc22e74af28d10fb2008a062b37349/cuda_tile-1.4.0-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:fc74185efd81f6153af0a19549d111dec6861ee9b9bc27927a2cef6e19173eb5", size = 280958, upload-time = "2026-05-27T17:46:53.061Z" }, + { url = "https://files.pythonhosted.org/packages/11/0b/4770f9e36b8108ce8c9078f71eb21c65e594d79c0770dd38daa045cfbd6c/cuda_tile-1.4.0-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:45be74f6568c440446f510bc7799b953858e64c6abf26e96f2c9598a79084860", size = 282508, upload-time = "2026-05-27T17:45:18.515Z" }, + { url = "https://files.pythonhosted.org/packages/a1/67/41f1acdf21bf6214a3a1c3b46d39b8eb0f9eba7aecc6b57005db35d56f9a/cuda_tile-1.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:edd1df4d7955032c7be2a26c6d7e47261415ba7c87587705e0f4f1fd0d61650a", size = 269783, upload-time = "2026-05-27T17:47:16.631Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c6/46a329f4c56ce54471784366394e235804423df2531307e14112e4636c76/cuda_tile-1.4.0-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:738593650784ebb3c601486914b563e7569144fe596048766ea9e12280ac3bb9", size = 281208, upload-time = "2026-05-27T17:46:48.325Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fb/bf3849ad68b1858ba50e6992863d266892d7d7db02d11c485c26cd090a1b/cuda_tile-1.4.0-cp314-cp314-manylinux2014_x86_64.whl", hash = "sha256:4b1a591c26836a550c2bf87c22d31c4716e5f83d24d255f843d9429625cca973", size = 282630, upload-time = "2026-05-27T17:45:10.789Z" }, + { url = "https://files.pythonhosted.org/packages/61/bb/211c0d5121230ee76cfc1a9ee107ec28aaae9e6ffb43a04aa172d0d4f4dc/cuda_tile-1.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19e10fe70ba92709b6ca446d1c52a8a346b56f4f8ad7c8941736f60e32f3c87", size = 270644, upload-time = "2026-05-27T17:45:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/f7f1dfa4d1ee7cc5b69e11d756be6ffec1561a5c7e3836fd0f71ca49adcf/cuda_tile-1.4.0-cp314-cp314t-manylinux2014_aarch64.whl", hash = "sha256:b3cbeffbe0fedac4936edcf00b6ba13ab5ddb74d3b7ce4a287dfc04491b5f6af", size = 283249, upload-time = "2026-05-27T17:46:12.032Z" }, + { url = "https://files.pythonhosted.org/packages/18/c0/fee527a085fca414fc993769912eb8ba2e15ce388f3168b868706e6d4c61/cuda_tile-1.4.0-cp314-cp314t-manylinux2014_x86_64.whl", hash = "sha256:675b2afff62af5d4e72c34bc72d0be27b0933a44933b8a449f590fbded8c1107", size = 284336, upload-time = "2026-05-27T17:44:59.489Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ab/0883194457932150a5ad334d609ac17bd704345974d21c8bae6ea251e7ed/cuda_tile-1.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3f58eac5577ea3ed7c17bfcab015a506fd2cf61f8848407c5b403f1bf46c55ca", size = 275861, upload-time = "2026-05-27T17:46:36.285Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", version = "13.0.96", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", version = "13.0.88", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "cyclopts" +version = "4.16.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docstring-parser" }, + { name = "rich" }, + { name = "rich-rst" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/07/bf61d13de86d96a4c46aff00c9ca0eced44bcc8c3e16280605c1253e5720/cyclopts-4.16.1.tar.gz", hash = "sha256:8aa47bf92a5fb33abca5af05e576eecdb0d2f79893ad29238046df78370fc4a8", size = 181196, upload-time = "2026-05-25T15:29:08.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/8d/7f362c2fb8ef4decd2160bc24d4292c6ca658cc6d9a161b89ca5122bbdbf/cyclopts-4.16.1-py3-none-any.whl", hash = "sha256:617795392c4113a2c2cc7af716f20244900e87f23daa05442d1268d81472a592", size = 219020, upload-time = "2026-05-25T15:29:09.646Z" }, +] + +[[package]] +name = "datasets" +version = "4.8.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/34/14cd8e76f907f7d4dca2334cfeec9f81d30fd15c25a015f99aaea694eaed/datasets-4.8.5.tar.gz", hash = "sha256:0f0c1c3d56ffff2c93b2f4c63c95bac94f3d7e8621aea2a2a576275233bba772", size = 605649, upload-time = "2026-04-27T15:43:57.384Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/99/00f3196036501b53032c4b1ab8337a0b978dee832ed276dae3815df4e8b5/datasets-4.8.5-py3-none-any.whl", hash = "sha256:5079900781719c0e063a8efdd2cd95a31ad0c63209178669cd23cf1b926149ff", size = 528973, upload-time = "2026-04-27T15:43:53.702Z" }, +] + +[[package]] +name = "depyf" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astor" }, + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/35/83fb0178212279aa0af031031905804c6de5618435d229f41ed21bb9ad2c/depyf-0.20.0.tar.gz", hash = "sha256:fb7683bd72c44f67b56029df2c47721e9a02ffa4d7b19095f1c54c4ebf797a98", size = 6168761, upload-time = "2025-10-13T12:33:38.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/65/4df6936130b56e1429114e663e7c1576cf845f3aef1b2dd200c0a5d19dba/depyf-0.20.0-py3-none-any.whl", hash = "sha256:d31effad4261cebecb58955d832e448ace88f432328f95f82fd99c30fd9308d4", size = 39381, upload-time = "2025-10-13T12:33:33.647Z" }, +] + +[[package]] +name = "detect-installer" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/ce/6897d812825e9d4c53e3c7112726e800cc5231b013b2223bf64f653ff362/detect_installer-0.1.0.tar.gz", hash = "sha256:00ad7ba0a36e3cf7d08a40d3643011746dbc112597c7d475cc91c416710ca4e7", size = 3049, upload-time = "2026-02-23T10:40:22.567Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/34/8cc73273414405086c58852916e4031812a6a30fe04c057e37ad99397b7f/detect_installer-0.1.0-py3-none-any.whl", hash = "sha256:034fb20fd665c36e6ba52b8821525ea07fb4f7f938cac459df889fb33801528a", size = 4539, upload-time = "2026-02-23T10:40:23.807Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "diskcache" +version = "5.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, +] + +[[package]] +name = "einops" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/77/850bef8d72ffb9219f0b1aac23fbc1bf7d038ee6ea666f331fa273031aa2/einops-0.8.2.tar.gz", hash = "sha256:609da665570e5e265e27283aab09e7f279ade90c4f01bcfca111f3d3e13f2827", size = 56261, upload-time = "2026-01-26T04:13:17.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/09/f8d8f8f31e4483c10a906437b4ce31bdf3d6d417b73fe33f1a8b59e34228/einops-0.8.2-py3-none-any.whl", hash = "sha256:54058201ac7087911181bfec4af6091bb59380360f069276601256a76af08193", size = 65638, upload-time = "2026-01-26T04:13:18.546Z" }, +] + +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + +[[package]] +name = "exceptiongroup" +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/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/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]] +name = "fastapi" +version = "0.136.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "email-validator" }, + { name = "fastapi-cli", extra = ["standard"] }, + { name = "fastar" }, + { name = "httpx" }, + { name = "jinja2" }, + { name = "pydantic-extra-types" }, + { name = "pydantic-settings" }, + { name = "python-multipart" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[[package]] +name = "fastapi-cli" +version = "0.0.24" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "rich-toolkit" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/58/74797ae9e4610cfa0c6b34c8309096d3b20bb29be3b8b5fbf1004d10fa5f/fastapi_cli-0.0.24.tar.gz", hash = "sha256:1afc9c9e21d7ebc8a3ca5e31790cd8d837742be7e4f8b9236e99cb3451f0de00", size = 19043, upload-time = "2026-02-24T10:45:10.476Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/4b/68f9fe268e535d79c76910519530026a4f994ce07189ac0dded45c6af825/fastapi_cli-0.0.24-py3-none-any.whl", hash = "sha256:4a1f78ed798f106b4fee85ca93b85d8fe33c0a3570f775964d37edb80b8f0edc", size = 12304, upload-time = "2026-02-24T10:45:09.552Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "fastapi-cloud-cli" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[[package]] +name = "fastapi-cloud-cli" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "detect-installer" }, + { name = "fastar" }, + { name = "httpx" }, + { name = "pydantic", extra = ["email"] }, + { name = "rich-toolkit" }, + { name = "rignore" }, + { name = "sentry-sdk" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/1d/57221a834b0f62dfa510c2b3db6e9b682cfbc280cef41919a8811ce1ff89/fastapi_cloud_cli-0.18.0.tar.gz", hash = "sha256:95f7a79200e3a90a005e068a4d8ede49d4b04accb095ccd4fd47da998fc28c74", size = 53320, upload-time = "2026-05-22T09:53:54.462Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/1e/1d54aabf71c003e89e73df92c3dfded311228e68db7cea5db90b3e0ef2b5/fastapi_cloud_cli-0.18.0-py3-none-any.whl", hash = "sha256:1f136fc651b0b6e2f4a9679e23c56e1c3be3405e74469c14ba6e2d5b87fdc113", size = 37087, upload-time = "2026-05-22T09:53:53.001Z" }, +] + +[[package]] +name = "fastar" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/0f/0aeb3fc50046617702acc0078b277b58367fd62eb727b9ec733ae0e8bbcc/fastar-0.11.0.tar.gz", hash = "sha256:aa7f100f7313c03fdb20f1385927ba95671071ba308ad0c1763fef295e1895ce", size = 70238, upload-time = "2026-04-13T17:11:17.143Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/06/a5773706afc8bd496769786590bbc56d2d0ee419a299cc12ea3f5717fcf3/fastar-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3c51f1c2cdddbd1420d2897ace7738e36c65e17f6ae84e0bfe763f8d1068bb97", size = 708394, upload-time = "2026-04-13T17:09:57.269Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a6/d5e2a4e48495616440a21eed07558219ca90243ad00b0502586f95bd4833/fastar-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d9d6b052baf5380baea866675dab6ccd04ec2460d12b1c46f10ce3f4ee6a820", size = 628417, upload-time = "2026-04-13T17:09:42.145Z" }, + { url = "https://files.pythonhosted.org/packages/ab/69/9816d69ac8265c9e50456637a487ccfb7a9c566efd9dbcd673df9c2558c2/fastar-0.11.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bd2f05666d4df7e14885b5c38fefd92a785917387513d33d837ff42ec143a22f", size = 863950, upload-time = "2026-04-13T17:09:11.506Z" }, + { url = "https://files.pythonhosted.org/packages/5b/0d/f88daad53aff2e754b6b5ff2a7113f72447a34f6ef17cc23ca99988117b7/fastar-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e6e74aba1ae77ca4aedcaf1697cd413319f4c88a5ccbe5b42c709517c5097e", size = 760737, upload-time = "2026-04-13T17:07:55.958Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a6/82ef4ecd969d50d92ed3ed9dbd8fe77faa24be5e5736f716edc9f4ce8d62/fastar-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38ef77fe940bbc9b37a98bd838727f844b11731cd39358a2640ff864fb385086", size = 757603, upload-time = "2026-04-13T17:08:10.623Z" }, + { url = "https://files.pythonhosted.org/packages/03/35/50249f0d827251f8ac511495e2eacccebda80a00a0ad73e9615b8113b84f/fastar-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8955e61b32d6aff82c983217abf80933fd823b0e727586fc72f08043d996fd59", size = 923952, upload-time = "2026-04-13T17:08:25.526Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d8/faee41659e9c379d906d24eaee6d6833ac8cfef0a5df480e5c2a8d3efb33/fastar-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:483532442cdb08fbff0169510224eae0836f2f672cea6aacb52847d90fefdc46", size = 816574, upload-time = "2026-04-13T17:08:56.076Z" }, + { url = "https://files.pythonhosted.org/packages/22/47/0448ea7992b997dad2bf004bfd98eca74b5858630eae080b50c7b17d9ddc/fastar-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef5a6071121e05d8287fc75bccb054bcbac8bb0501200a0c0a8feeace5303ea4", size = 819382, upload-time = "2026-04-13T17:09:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/33/ef/0d63eb43586831b7a6f8b22c4d77125a7c594423af1f4f090fa9541b9b40/fastar-0.11.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:e45e598af5afe8412197d4786efd6cf29be02e7d3d4f6a3461149eae5d7e94f1", size = 885254, upload-time = "2026-04-13T17:08:40.9Z" }, + { url = "https://files.pythonhosted.org/packages/01/25/edd584675d69e49a165052c3ee886df1c5d574f3e7d813c990306387c623/fastar-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e160919b1c47ddb8538e7e8eb4cd527281b40f0bf75110a75993838ef61f286", size = 971239, upload-time = "2026-04-13T17:10:12.997Z" }, + { url = "https://files.pythonhosted.org/packages/a5/37/e8bb24f506ba2b08fbaf36c5800e843bd4d542954e9331f00418e2d23349/fastar-0.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4bb4dc0fc8f7a6807febcebce8a2f3626ba4955a9263d81ecc630aad83be84c0", size = 1035185, upload-time = "2026-04-13T17:10:30.207Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bf/be753736296338149ee4cb3e92e2b5423d6ba17c7b951d15218fd7e99bbf/fastar-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4ec95af56aa173f6e320e1183001bf108ba59beaf13edd1fc8200648db203588", size = 1072191, upload-time = "2026-04-13T17:10:47.072Z" }, + { url = "https://files.pythonhosted.org/packages/d2/cd/a81c1aaafb5a22ce57c98ae22f39c89413ed53e4ee6e1b1444b0bd666a6c/fastar-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:136cf342735464091c39dc3708168f9fdeb9ebea40b1ead937c61afaf46143d9", size = 1028054, upload-time = "2026-04-13T17:11:04.293Z" }, + { url = "https://files.pythonhosted.org/packages/ec/88/1ce4eed3d70627c95f49ca017f6bbbf2ddcc4b0c601d293259de7689bc20/fastar-0.11.0-cp312-cp312-win32.whl", hash = "sha256:35f23c11b556cc4d3704587faacbc0037f7bdf6c4525cd1d09c70bda4b1c6809", size = 454198, upload-time = "2026-04-13T17:11:45.168Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1d/26ce92f4331cd61a69840db9ca6115829805eec24f285481a854f578e917/fastar-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:920bc56c3c0b8a8ca492904941d1883c1c947c858cd93343356c29122a38f44c", size = 486697, upload-time = "2026-04-13T17:11:31.084Z" }, + { url = "https://files.pythonhosted.org/packages/ed/96/e6eda4480559c69b05d466e7b5ea9170e81fef3795a73e059959a3258319/fastar-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:395248faf89e8a6bd5dc1fd544c8465113b627cb6d7c8b296796b60ebea33593", size = 462591, upload-time = "2026-04-13T17:11:20.577Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d6/3be260037e86fb694e88d47f583bac3a0188c99cee1a6b257ac26cb6b53c/fastar-0.11.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:33f544b08b4541b678e53749b4552a44720d96761fb79c172b005b1089c443ed", size = 707975, upload-time = "2026-04-13T17:09:58.866Z" }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7867aefb1784662554a335f2952c75a50f0c70585ed0d2210d6cc15e5627/fastar-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c1c792447e4a642745f347ff9847c52af39633071c57ee67ed53c157fc3506", size = 628460, upload-time = "2026-04-13T17:09:43.776Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2b/d11d84bdd5e0e377771b955755771e3460b290da5809cb78c1b735ee2228/fastar-0.11.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:881247e6b6eaea59fc6569f9b61447aa6b9fc2ee864e048b4643d69c52745805", size = 863054, upload-time = "2026-04-13T17:09:13.048Z" }, + { url = "https://files.pythonhosted.org/packages/25/39/d3f428b318fa940b1b6e785b8d54fc895dfb5d5b945ef8d5442ffa904fb2/fastar-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:863b7929845c9fec92ef6c8d59579cf46af5136655e5342f8df5cebe46cab06c", size = 760247, upload-time = "2026-04-13T17:07:57.396Z" }, + { url = "https://files.pythonhosted.org/packages/9e/04/03949aee82aabb8ede06ac5a4a5579ffaf98a8fe59ce958494508ff15513/fastar-0.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:96b4a57df12bf3211662627a3ea29d62ecb314a2434a0d0843f9fc23e47536e5", size = 756512, upload-time = "2026-04-13T17:08:12.415Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0c/2ca1ae0a3828ca51047962d932b80daca2522db73e8cb9d040cb6ebe28d5/fastar-0.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceef1c2c4df7b7b8ebd3f5d718bbf457b9bbdf25ce0bd07870211ec4fbd9aff4", size = 922183, upload-time = "2026-04-13T17:08:27.187Z" }, + { url = "https://files.pythonhosted.org/packages/65/68/7fe808b1f73a68e686f25434f538c6dc10ef4dfb3db0ace22cd861744bf8/fastar-0.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8e545918441910a779659d4759ad0eef349e935fbdb4668a666d3681567eb05", size = 816394, upload-time = "2026-04-13T17:08:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/1f/17/07d086080f8a83b8d7966955e29bcdbd6a060f5bd949dc9d5abd3658cead/fastar-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28095bb8f821e85fc2764e1a55f03e5e2876dee2abe7cd0ee9420d929905d643", size = 818983, upload-time = "2026-04-13T17:09:28.46Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e2/2c4edf0910af2e814ff6d65b77a91196d472ca8a9fb2033bd983f6856caa/fastar-0.11.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0fafb95ecbe70f666a5e9b35dd63974ccdc9bb3d99ccdbd4014a823ec3e659b5", size = 884689, upload-time = "2026-04-13T17:08:42.763Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/04fdcbd6558e60de4ced3b55230fac47675d181252582b2fcec3c74608e5/fastar-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:af48fed039b94016629dcdad1c95c90c486326dd068de2b0a4df419ee09b6821", size = 970677, upload-time = "2026-04-13T17:10:15.124Z" }, + { url = "https://files.pythonhosted.org/packages/df/b3/2b860a9658550167dbd5824c85e88d0b4b912bf493e42a6322544d6e483d/fastar-0.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:74cd96163f39b8638ab4e8d49708ca887959672a22871d8170d01f067319533b", size = 1034026, upload-time = "2026-04-13T17:10:32.318Z" }, + { url = "https://files.pythonhosted.org/packages/b7/9b/fa42ea1188b144bac4b1b60753dfd449974a4d5eda132029ee7711569f94/fastar-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4e8b993cb5613bab495ed482810bedc0986633fcb9a3b55c37ec88e0d6714f6a", size = 1071147, upload-time = "2026-04-13T17:10:48.833Z" }, + { url = "https://files.pythonhosted.org/packages/95/c8/d2e501556dca9f1fbc9246111a31792fb49ad908fa4927f34938a97a3604/fastar-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfe39d91fc28e37e06162d94afe01050220edb7df554acb5b702b5503e564816", size = 1028377, upload-time = "2026-04-13T17:11:06.374Z" }, + { url = "https://files.pythonhosted.org/packages/db/33/5f11f23eca0a569cd052507bc45dda2e5468697f8665728d25be44120f7d/fastar-0.11.0-cp313-cp313-win32.whl", hash = "sha256:c5f63d4d99ff4bfb37c659982ec413358bdee747005348756cc50a04d412d989", size = 454089, upload-time = "2026-04-13T17:11:46.821Z" }, + { url = "https://files.pythonhosted.org/packages/da/2f/35ff03c939cba7a255a9132367873fec6c355fd06a7f84fedcbaf4c8129f/fastar-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8690ed1928d31ded3ada308e1086525fb3871f5fa81e1b69601a3f7774004583", size = 486312, upload-time = "2026-04-13T17:11:32.86Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/ee9246cbfcbfd4144558f35e7e9a306ffe0a7564730a5188c45f21d2dab8/fastar-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:d977ded9d98a0719a305e0a4d5ee811f1d3e856d853a50acb8ae833c3cd6d5d2", size = 461975, upload-time = "2026-04-13T17:11:22.589Z" }, + { url = "https://files.pythonhosted.org/packages/7a/cd/3644c48ecac456f928c12d47ec3bed36c36555b17c3859856f1ff860265d/fastar-0.11.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:71375bd6f03c2a43eb47bd949ea38ff45434917f9cdac79675c5b9f60de4fa73", size = 707860, upload-time = "2026-04-13T17:10:00.371Z" }, + { url = "https://files.pythonhosted.org/packages/69/ca/dee04476ae3626b2b040a60ad84628f77e1ffd8444232f2426b0ca1e0d7e/fastar-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:eddfd9cab16e19ae247fe44bf992cb403ccfe27d3931d6de29a4695d95ad386c", size = 628216, upload-time = "2026-04-13T17:09:45.355Z" }, + { url = "https://files.pythonhosted.org/packages/dc/5e/9395c7353d079cb4f5be0f7982ce0dc9f2e7dec5fd175eef466729d6023a/fastar-0.11.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c371f1d4386c699018bb64eb2fa785feacf32785559049d2bb72fe4af023f53", size = 864378, upload-time = "2026-04-13T17:09:14.611Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/1e4f67148223ff219612b6281a6000357abbcc2417964fa5c83f11d68fce/fastar-0.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cad7fa41e3e66554387481c1a09365e4638becd322904932674159d5f4046728", size = 760921, upload-time = "2026-04-13T17:07:59.138Z" }, + { url = "https://files.pythonhosted.org/packages/0f/82/09d11fb6d12f17993ffaf32ffd30c3c121a11e2966e84f19fb6f66430118/fastar-0.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf36652fa71b83761717c9899b98732498f8a2cb6327ff16bbf07f6be85c3437", size = 757012, upload-time = "2026-04-13T17:08:14.186Z" }, + { url = "https://files.pythonhosted.org/packages/52/1f/5aeeacc4cb65615e2c9292cd9c5b0cd6fb6d2e6ee472ca6adc6c1b1b22ef/fastar-0.11.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f68ff8c17833053da4841720e95edde80ce45bb994b6b7d51418dddaac70ee47", size = 924510, upload-time = "2026-04-13T17:08:28.741Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1a/1e5bdabbeaf2e856928956292609f2ff6a650f94480fb8afaca30229e483/fastar-0.11.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4563ed37a12ea1cdc398af8571258d24b988bf342b7b3bf5451bd5891243280c", size = 816602, upload-time = "2026-04-13T17:08:59.461Z" }, + { url = "https://files.pythonhosted.org/packages/87/24/f960147910da3bed41a3adfcb026e17d5f50f4cf467a3324237a7088f61a/fastar-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cee63c9875cba3b70dc44338c560facc5d6e763047dcc4a30501f9a68cf5f890", size = 819452, upload-time = "2026-04-13T17:09:29.926Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f4/3e77d7901d5707fd7f8a352e153c8ae09ea974e6fabad0b7c4eb9944b8d4/fastar-0.11.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:bd76bfffae6d0a91f4ac4a612f721e7aec108db97dccdd120ae063cd66959f27", size = 885254, upload-time = "2026-04-13T17:08:44.285Z" }, + { url = "https://files.pythonhosted.org/packages/47/01/1585edd5ec47782ae93cd94edf05828e0ab02ef00aec00aea4194a600464/fastar-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f5b707501ec01c1bc0518f741f01d322e50c9adc19a451aa24f67a2316e9397", size = 971496, upload-time = "2026-04-13T17:10:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e9/6874c9d1236ded565a0bed54b320ac9f165f287b1d89490fb70f9f323c81/fastar-0.11.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:37c0b5a88a657839aad98b0a6c9e4ac4c2c15d6b49c44ee3935c6b08e9d3e479", size = 1034685, upload-time = "2026-04-13T17:10:34.063Z" }, + { url = "https://files.pythonhosted.org/packages/14/d8/4ab20613ce2983427aee958e39be878dba874aa227c530a845e32429c4f6/fastar-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6c55f536c62a6efb180c1af0d5182948bff576bbfe6276e8e1359c9c7d2215d8", size = 1072675, upload-time = "2026-04-13T17:10:50.53Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ae/5ac3b7c20ce4b08f011dd2b979f96caabe64f9b10b157f211ea91bdfadca/fastar-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3082eeca59e189b9039335862f4c2780c0c8871d656bfdf559db4414a105b251", size = 1029330, upload-time = "2026-04-13T17:11:08.138Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e7/37cd6a1d4e288292170b64e19d79ecce2a7de8bb76790323399a2abc4619/fastar-0.11.0-cp314-cp314-win32.whl", hash = "sha256:b201a0a4e29f9fec2a177e13154b8725ec65ab9f83bd6415483efaa2aa18344b", size = 453940, upload-time = "2026-04-13T17:11:48.713Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1c/795c878b1ee29d79021cf8ed81f18f2b25ccde58453b0d34b9bdc7e025ea/fastar-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:868fddb26072a43e870a8819134b9f80ee602931be5a76e6fb873e04da343637", size = 486334, upload-time = "2026-04-13T17:11:34.882Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a4/113f104301df8bddcc0b3775b611a30cb7610baa3add933c7ccac9386467/fastar-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:3db39c9cc42abb0c780a26b299f24dfbc8be455985e969e15336d70d7b2f833b", size = 461534, upload-time = "2026-04-13T17:11:24.329Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a6/5c5f2c2c8e0c63e56a5636ebc7721589c889e94c0092cec7eb28ae7207e6/fastar-0.11.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:49c3299dec5e125e7ebaa27545714da9c7391777366015427e0ae62d548b442b", size = 707156, upload-time = "2026-04-13T17:10:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/df/f7/982c01b61f0fc135ad2b16d01e6d0ee53cf8791e68827f5f7c5a65b2e5b1/fastar-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3328ed1ed56d31f5198350b17dd60449b8d6b9d47abb4688bab6aef4450a165b", size = 627032, upload-time = "2026-04-13T17:09:46.978Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c3/38f1dac77ae0c71c37b176277c96d830796b8ce2fe69705f917829b53829/fastar-0.11.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bd3eca3bbfec84a614bcb4143b4ad4f784d0895babc26cfc88436af88ca23c7a", size = 864403, upload-time = "2026-04-13T17:09:16.58Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f0/e69c363bdb3e5a5848e937b662b5469581ee6682c51bc1c0556494773929/fastar-0.11.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff86a967acb0d621dd24063dda090daa67bf4993b9570e97fe156de88a9006ca", size = 759480, upload-time = "2026-04-13T17:08:00.599Z" }, + { url = "https://files.pythonhosted.org/packages/3b/29/4d8737590c2a6357d614d7cc7288e8f68e7e449680b8922997cc4349e65e/fastar-0.11.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:86eaf7c0e985d93a7734168be2fb232b2a8cca53e41431c2782d7c12b12c03b1", size = 756219, upload-time = "2026-04-13T17:08:15.699Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ec/400de7b3b7d48801908f19cf5462177104395799472671b3e8152b2b04ca/fastar-0.11.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91f07b0b8eb67e2f177733a1f884edad7dfb9f8977ffef15927b20cb9604027d", size = 923669, upload-time = "2026-04-13T17:08:30.574Z" }, + { url = "https://files.pythonhosted.org/packages/5d/01/8926c53da923fed7ab4b96e7fbf7f73b663beb4f02095b654d6fab46f9ad/fastar-0.11.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f85c896885eb4abf1a635d54dea22cac6ae48d04fc2ea26ae652fcf1febe1220", size = 815729, upload-time = "2026-04-13T17:09:01.204Z" }, + { url = "https://files.pythonhosted.org/packages/89/f0/5fef4c7946e352651b504b1a4235dac3505e7cfd24020788ab50552e84bf/fastar-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:075c07095c8de4b774ba8f28b9c0a02b1a2cd254da50cbe464dd3bb2432e9158", size = 819812, upload-time = "2026-04-13T17:09:31.907Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c8/0ebc3298b4a45e7bddc50b169ae6a6f5b80c939394d4befe6e60de535ee7/fastar-0.11.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:07f028933820c65750baf3383b807ecce1cd9385cf00ce192b79d263ad6b856c", size = 884074, upload-time = "2026-04-13T17:08:45.802Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9f/7baa4cdff8d6fbca41fa5c764b48a941fed8a9ec6c4cc92de65895a28299/fastar-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:039f875efa0f01fa43c20bf4e2fc7305489c61d0ac76eda991acfba7820a0e63", size = 969450, upload-time = "2026-04-13T17:10:18.667Z" }, + { url = "https://files.pythonhosted.org/packages/d4/dc/1ebbfb58a47056ba866494f19efbcdd2ba2897096b94f36e796594b4d05b/fastar-0.11.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:fff12452a9a5c6814a012445f26365541cc3d99dcca61f09762e6a389f7a32ea", size = 1033775, upload-time = "2026-04-13T17:10:36.165Z" }, + { url = "https://files.pythonhosted.org/packages/c2/5f/ce4e3914066f08c99eb8c32952cc07c1a013e81b1db1b0f598130bf6b974/fastar-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2bf733e09f942b6fa876efe30a90508d1f4caef5630c00fb2a84fba355873712", size = 1072158, upload-time = "2026-04-13T17:10:52.497Z" }, + { url = "https://files.pythonhosted.org/packages/03/2a/6bca72992c84151c387cc6558f3867f5ebe5fb3684ee6fa9b76280ba4b8e/fastar-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d1531fa848fdd3677d2dce0a4b436ea64d9ae38fb8babe2ddbc180dd153cb7a3", size = 1028577, upload-time = "2026-04-13T17:11:09.934Z" }, + { url = "https://files.pythonhosted.org/packages/83/18/7a7c15657a3da5569b26fc51cde6a80f8d84cb54b3b1aea6d74a103db4ad/fastar-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:5744551bc67c6fc6581cbd0e34a0fd6e2cd0bd30b43e94b1c3119cf35064b162", size = 453601, upload-time = "2026-04-13T17:11:53.726Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d8/331b59a6de279f3ad75c10c02c40a12f21d64a437d9c3d6f1af2dcbd7a76/fastar-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f4ce44e3b56c47cf38244b98d29f269b259740a580c47a2552efa5b96a5458fb", size = 486436, upload-time = "2026-04-13T17:11:40.089Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fd/5390ec4f49100f3ecb9968a392f9e6d039f1e3fe0ecd28443716ff01e589/fastar-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:76c1359314355eafbc6989f20fb1ad565a3d10200117923b9da765a17e2f6f11", size = 461049, upload-time = "2026-04-13T17:11:25.918Z" }, +] + +[[package]] +name = "fastmcp" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastmcp-slim", extra = ["client", "server"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/a9/5c5a01b6abd5346bf60b97cfd29e4a86661940c27dd562bfcda07fd03519/fastmcp-3.3.1.tar.gz", hash = "sha256:979362ea557de42a5f40342563c7e4b236bcc8e7cd192715f50030695d1a71cd", size = 28681699, upload-time = "2026-05-15T15:50:39.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/11/6b1bdada6ccfe647d615ae63f9106f8136aec17971e9361546af01c7d38e/fastmcp-3.3.1-py3-none-any.whl", hash = "sha256:862440c5c4d281363a5995eee59d77f0f7cac1f18869038729cecf03b02fc522", size = 7903, upload-time = "2026-05-15T15:50:36.424Z" }, +] + +[[package]] +name = "fastmcp-slim" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pydantic", extra = ["email"] }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/a0/627103e517e1d0d6f1eec633d5662d13e776f01b45ad188e4f5f7478b438/fastmcp_slim-3.3.1.tar.gz", hash = "sha256:0957835fc59452e143ab2f4b7836d2d2df9b2d9958408edc79ba8b56232b2a88", size = 567007, upload-time = "2026-05-15T15:50:10.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/ee/97047f4cc2d7b1d46670d08d8ad01a96e7a748cc01c0b4b351ad8eddbc7a/fastmcp_slim-3.3.1-py3-none-any.whl", hash = "sha256:6cf1c2d77e3adb0d409d6825ed6b0b2a999062973e00b8eea03bd48bf9b4c043", size = 738644, upload-time = "2026-05-15T15:50:08.336Z" }, +] + +[package.optional-dependencies] +client = [ + { name = "authlib" }, + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "mcp" }, + { name = "opentelemetry-api" }, + { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, +] +server = [ + { name = "authlib" }, + { name = "cyclopts" }, + { name = "exceptiongroup" }, + { name = "griffelib" }, + { name = "httpx" }, + { name = "jsonref" }, + { name = "jsonschema-path" }, + { name = "mcp" }, + { name = "openapi-pydantic" }, + { name = "opentelemetry-api" }, + { name = "packaging" }, + { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, + { name = "pyperclip" }, + { name = "python-multipart" }, + { name = "pyyaml" }, + { name = "uncalled-for" }, + { name = "uvicorn" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "fastsafetensors" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/33/c97b2bcbe06e0f011eedee0f41d4060f6344901a53c2703acc3dd7429713/fastsafetensors-0.3.2.tar.gz", hash = "sha256:9e358fce238684613a5c3ebb7800c52c5b3270c0bb5e4ed2191ee8f3d0431de1", size = 70409, upload-time = "2026-05-22T05:39:34.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/68/a31c1661adf4d1b5ec29470ff991bde9094e4f347b0e6d1af8ba6b560d32/fastsafetensors-0.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a932d7166c9e17e48aca3e5503d326bc6fc73fce6dc985ae6bd2ccc0f308b14", size = 1907188, upload-time = "2026-05-22T05:39:30.242Z" }, + { url = "https://files.pythonhosted.org/packages/e4/43/57fd9ee68a39f1a5fba0dd9be6b62f14460bab532840eb8198202fd73d30/fastsafetensors-0.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41297f01d3e2585e86fbc56df499f140b233ec8d6cb17c3f95b5e81a8b98a53e", size = 1906826, upload-time = "2026-05-22T05:39:31.805Z" }, + { url = "https://files.pythonhosted.org/packages/09/6f/cafbb1e1089593c919f8a73575e1b332c111aa990e8f07844fee4246dd07/fastsafetensors-0.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a50da42cf2d85cc8a50f945126decb4ce18aa4151506dd416482dd50a613fa97", size = 1904609, upload-time = "2026-05-22T05:39:33.322Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "fla-core" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "einops" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/14/2aabd37839b9f3c6a67fbc5678f906d04d0c242c603ac234eefe02df99a6/fla_core-0.5.0.tar.gz", hash = "sha256:476dd94711702af81cc4827010d9209f6053d8cdceac8e43d3c8497071f07a81", size = 418171, upload-time = "2026-04-21T20:25:40.948Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/03/96e6820d176256353670b41ca56dabbbebe129674b4f4ad7b54a152b7b36/fla_core-0.5.0-py3-none-any.whl", hash = "sha256:5c826ff32daf6b629658e3e4f6125d87cf8c32eea937e3be9ba85f51951d809a", size = 595276, upload-time = "2026-04-21T20:25:37.698Z" }, +] + +[[package]] +name = "flash-linear-attention" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fla-core" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/5c/1db76cc829c951117a3112f306d50333bd71399d2e35807fe7c99ffc2007/flash_linear_attention-0.5.0.tar.gz", hash = "sha256:22b789a47f07738b4382ecdf775d7bb40e0d803c467c34f8e2ecd6a1dc780938", size = 160419, upload-time = "2026-04-21T20:25:42.344Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/16/7736db08806981562c728f32ea1dcb4565948fa9faffdbf4ffbf72522fbf/flash_linear_attention-0.5.0-py3-none-any.whl", hash = "sha256:92e64e989ed34355c1f838232597b2e39783ee0494ada3199b58e156aa1d8eb8", size = 319037, upload-time = "2026-04-21T20:25:39.473Z" }, +] + +[[package]] +name = "flashinfer-cubin" +version = "0.6.11.post2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/96/da75a9f61c64c87b16baa339fc8216a6c3743c5d263c555fded30fcbe6f7/flashinfer_cubin-0.6.11.post2-py3-none-any.whl", hash = "sha256:eb01c2801ee31d145bbf7afb2c223150333e602c8208216017b0190b1087b990", size = 360908523, upload-time = "2026-05-14T04:57:41.355Z" }, +] + +[[package]] +name = "flashinfer-python" +version = "0.6.11.post2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "apache-tvm-ffi" }, + { name = "click" }, + { name = "cuda-tile" }, + { name = "einops" }, + { name = "ninja" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "nvidia-cudnn-frontend" }, + { name = "nvidia-cutlass-dsl" }, + { name = "nvidia-ml-py" }, + { name = "packaging" }, + { name = "requests" }, + { name = "tabulate" }, + { name = "torch" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/53/dbf2157f2bbb96d6f7a6891cf6abfb2e6e18963760a0c53e96c2de5c59db/flashinfer_python-0.6.11.post2.tar.gz", hash = "sha256:e9fdac56aea9f0f58a4e69b0645c54993760d3cc6c7bf5c2df4ce5a0aecc7953", size = 9248515, upload-time = "2026-05-14T04:57:32.83Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/bc/518b092473f37d904ae07766ad37c772b93da13ea788777b22a80c3f1a7c/flashinfer_python-0.6.11.post2-py3-none-any.whl", hash = "sha256:550cbdb760f9f7ec0e42055e06636b9489d05f1a38989cafd77e6eb820de0138", size = 13746417, upload-time = "2026-05-14T04:57:30.25Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gguf" +version = "0.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/ae/17f1308ae45cd7b08ebb521747d5b23f4efc4d172038a4e228dd5106c3ff/gguf-0.19.0.tar.gz", hash = "sha256:dbadcd6cc7ccd44256f2229fe7c2dff5e8aa5cf0612ab987fd2b1a57e428923f", size = 111220, upload-time = "2026-05-06T13:04:03.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/bb/d71d6da82763528c2c2ed6b59a9d6142c6595545a4c448e2085d155e88c2/gguf-0.19.0-py3-none-any.whl", hash = "sha256:70bcd10edfe697fb2dad6e40af2234b9d8ece9a41a99761405121ebda1c3c1cd", size = 118475, upload-time = "2026-05-06T13:04:02.588Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.75.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035, upload-time = "2026-05-07T08:04:49.423Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, +] + +[[package]] +name = "gradio" +version = "6.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "audioop-lts", marker = "python_full_version >= '3.13'" }, + { name = "brotli" }, + { name = "fastapi" }, + { name = "gradio-client" }, + { name = "groovy" }, + { name = "hf-gradio" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "orjson" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "pydub" }, + { name = "python-multipart" }, + { name = "pytz" }, + { name = "pyyaml" }, + { name = "safehttpx" }, + { name = "semantic-version" }, + { name = "starlette" }, + { name = "tomlkit" }, + { name = "typer" }, + { name = "typing-extensions" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/6d/a8b16b2bd3547c9d9da9656242d350481ffbedc97e3d8fc4eb939448be55/gradio-6.15.2.tar.gz", hash = "sha256:67433b1889dc8526658196277ca9e34109f73aa65656635c0dc6afb542571f55", size = 36432997, upload-time = "2026-05-28T19:25:52.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/43/2f9827ea758ef70fcc23e2c468dd5ef208352b1a56d4446d680df2e4fc1a/gradio-6.15.2-py3-none-any.whl", hash = "sha256:3492eee59cbe4afd21e8f458fc229f0d92c17bdaacaefaf9580dd63a6406c82d", size = 20094003, upload-time = "2026-05-28T19:25:48.489Z" }, +] + +[[package]] +name = "gradio-client" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "packaging" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/e6/6b6029f5fe2ad7f1211105d530e34d991014c2cae463f9223033031cfc4f/gradio_client-2.5.0.tar.gz", hash = "sha256:4cde99bad62149595c30c90876ca2e405e3a13687ecf895474f3412cb476673d", size = 59013, upload-time = "2026-04-20T23:16:21.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/81/0a861b8e1ff42960139c6cd4c7dd591292fa09ea1ae2d87677441cba4c00/gradio_client-2.5.0-py3-none-any.whl", hash = "sha256:d43e2179c29076292a76485ad7ed2e6eaa19d14ac58283bd7f5beabfe4ca958c", size = 59952, upload-time = "2026-04-20T23:16:20.186Z" }, +] + +[[package]] +name = "griffelib" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, +] + +[[package]] +name = "groovy" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/36/bbdede67400277bef33d3ec0e6a31750da972c469f75966b4930c753218f/groovy-0.1.2.tar.gz", hash = "sha256:25c1dc09b3f9d7e292458aa762c6beb96ea037071bf5e917fc81fb78d2231083", size = 17325, upload-time = "2025-02-28T20:24:56.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/27/3d6dcadc8a3214d8522c1e7f6a19554e33659be44546d44a2f7572ac7d2a/groovy-0.1.2-py3-none-any.whl", hash = "sha256:7f7975bab18c729a257a8b1ae9dcd70b7cafb1720481beae47719af57c35fa64", size = 14090, upload-time = "2025-02-28T20:24:55.152Z" }, +] + +[[package]] +name = "grpcio" +version = "1.80.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/e8/a2b749265eb3415abc94f2e619bbd9e9707bebdda787e61c593004ec927a/grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0", size = 6015616, upload-time = "2026-03-30T08:47:13.428Z" }, + { url = "https://files.pythonhosted.org/packages/3e/97/b1282161a15d699d1e90c360df18d19165a045ce1c343c7f313f5e8a0b77/grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2", size = 12014204, upload-time = "2026-03-30T08:47:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/d319c6e997b50c155ac5a8cb12f5173d5b42677510e886d250d50264949d/grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de", size = 6563866, upload-time = "2026-03-30T08:47:18.588Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f6/fdd975a2cb4d78eb67769a7b3b3830970bfa2e919f1decf724ae4445f42c/grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921", size = 7273060, upload-time = "2026-03-30T08:47:21.113Z" }, + { url = "https://files.pythonhosted.org/packages/db/f0/a3deb5feba60d9538a962913e37bd2e69a195f1c3376a3dd44fe0427e996/grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411", size = 6782121, upload-time = "2026-03-30T08:47:23.827Z" }, + { url = "https://files.pythonhosted.org/packages/ca/84/36c6dcfddc093e108141f757c407902a05085e0c328007cb090d56646cdf/grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd", size = 7383811, upload-time = "2026-03-30T08:47:26.517Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ef/f3a77e3dc5b471a0ec86c564c98d6adfa3510d38f8ee99010410858d591e/grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f", size = 8393860, upload-time = "2026-03-30T08:47:29.439Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8d/9d4d27ed7f33d109c50d6b5ce578a9914aa68edab75d65869a17e630a8d1/grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f", size = 7830132, upload-time = "2026-03-30T08:47:33.254Z" }, + { url = "https://files.pythonhosted.org/packages/14/e4/9990b41c6d7a44e1e9dee8ac11d7a9802ba1378b40d77468a7761d1ad288/grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193", size = 4140904, upload-time = "2026-03-30T08:47:35.319Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2c/296f6138caca1f4b92a31ace4ae1b87dab692fc16a7a3417af3bb3c805bf/grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff", size = 4880944, upload-time = "2026-03-30T08:47:37.831Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" }, + { url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" }, + { url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" }, + { url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830, upload-time = "2026-03-30T08:47:49.643Z" }, + { url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216, upload-time = "2026-03-30T08:47:52.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866, upload-time = "2026-03-30T08:47:55.687Z" }, + { url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602, upload-time = "2026-03-30T08:47:58.303Z" }, + { url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752, upload-time = "2026-03-30T08:48:01.311Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310, upload-time = "2026-03-30T08:48:04.594Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" }, + { url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376, upload-time = "2026-03-30T08:48:10.005Z" }, + { url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133, upload-time = "2026-03-30T08:48:12.927Z" }, + { url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748, upload-time = "2026-03-30T08:48:16.386Z" }, + { url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711, upload-time = "2026-03-30T08:48:19.627Z" }, + { url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372, upload-time = "2026-03-30T08:48:22.373Z" }, + { url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268, upload-time = "2026-03-30T08:48:25.638Z" }, + { url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000, upload-time = "2026-03-30T08:48:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477, upload-time = "2026-03-30T08:48:32.054Z" }, + { url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563, upload-time = "2026-03-30T08:48:34.538Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457, upload-time = "2026-03-30T08:48:37.308Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-gradio" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gradio-client" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/86/c9694b7cfada5780e75769e60dc161a161f4dd7fc91b61db5e3a3338bef9/hf_gradio-0.4.1.tar.gz", hash = "sha256:a017d942618f0d495a58ee4563047fa04bef614c00e0cb789a9a6d0633cffa7b", size = 6560, upload-time = "2026-04-22T14:01:32.334Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/2d/afff2ee87e75d8eb85c92bb8cf0e15b05c23c2ebd8fd8dec781d8601ed7f/hf_gradio-0.4.1-py3-none-any.whl", hash = "sha256:76b8cb8be6abe62d74c1ad2d35b42f0629db89aa9e1a8d033cecfe7c856eeab3", size = 4482, upload-time = "2026-04-17T19:53:31.827Z" }, +] + +[[package]] +name = "hf-sandbox" +version = "0.1.1" +source = { git = "https://github.com/rycerzes/hf-sandbox?rev=feat%2Fnamed-tunnels#94b4dc0bf17b113eebb4c0db697e52ac52592f97" } +dependencies = [ + { name = "cloudflare" }, + { name = "dnspython" }, + { name = "httpx" }, + { name = "huggingface-hub" }, +] + +[[package]] +name = "hf-xet" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/d8/5c06fc76461418326a7decf8367480c35be11a41fd938633929c60a9ec6b/hf_xet-1.5.0.tar.gz", hash = "sha256:e0fb0a34d9f406eed88233e829a67ec016bec5af19e480eac65a233ea289a948", size = 837196, upload-time = "2026-05-06T06:18:15.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/9b/6912c99070915a4f28119e3c5b52a9abd1eec0ad5cb293b8c967a0c6f5a2/hf_xet-1.5.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7d70fe2ce97b9db73b9c9b9c81fe3693640aec83416a966c446afea54acfae3c", size = 4023383, upload-time = "2026-05-06T06:17:53.947Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6d/9563cfde59b5d8128a9c7ec972a087f4c782e4f7bac5a85234edfd5d5e49/hf_xet-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:73a0dae8c71de3b0633a45c73f4a4a5ed09e94b43441d82981a781d4f12baa42", size = 3792751, upload-time = "2026-05-06T06:17:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/07/a5/ed5a0cf35b49a0571af5a8f53416dad1877a718c021c9937c3a53cb45781/hf_xet-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a60290ec57e9b71767fba7c3645ddafdd0759974b540441510c629c6db6db24a", size = 4456058, upload-time = "2026-05-06T06:17:40.735Z" }, + { url = "https://files.pythonhosted.org/packages/60/fb/3ae8bf2a7a37a4197d0195d7247fd25b3952e15cb8a599e285dfaa6f52b3/hf_xet-1.5.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e5de0f6deada0dada870bb376a11bcd1f08abf3a968a6d118f33e72d1b1eb480", size = 4250783, upload-time = "2026-05-06T06:17:38.412Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/8bae40d4d91525085137196e84eb0ed49cf65b5e96e5c3ecdadd8bd0fac2/hf_xet-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c799d49f1a5544a0ef7591c0ee75e0d6b93d6f56dc7a4979f59f7518d2872216", size = 4445594, upload-time = "2026-05-06T06:18:04.219Z" }, + { url = "https://files.pythonhosted.org/packages/13/59/c74efbbd4e8728172b2cc72a2bc014d2947a4b7bdced932fbd3f5da1a4e5/hf_xet-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2baea1b0b989e5c152fe81425f7745ddc8901280ba3d97c98d8cdece7b706c60", size = 4663995, upload-time = "2026-05-06T06:18:06.1Z" }, + { url = "https://files.pythonhosted.org/packages/73/32/8e1e0410af64cda9b139d1dcebdc993a8ff9c8c7c0e2696ae356d75ccc0d/hf_xet-1.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:526345b3ed45f374f6317349df489167606736c876241ba984105afe7fd4839d", size = 3966608, upload-time = "2026-05-06T06:18:19.74Z" }, + { url = "https://files.pythonhosted.org/packages/fc/34/a8febc8f4edbea8b3e21b02ebc8b628679b84ba7e45cde624a7736b51500/hf_xet-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:786d28e2eb8315d5035544b9d137b4a842d600c434bb91bf7d0d953cce906ad4", size = 3796946, upload-time = "2026-05-06T06:18:17.568Z" }, + { url = "https://files.pythonhosted.org/packages/2a/20/8fc8996afe5815fa1a6be8e9e5c02f24500f409d599e905800d498a4e14d/hf_xet-1.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:872d5601e6deea30d15865ede55d29eac6daf5a534ab417b99b6ef6b076dd96c", size = 4023495, upload-time = "2026-05-06T06:18:01.94Z" }, + { url = "https://files.pythonhosted.org/packages/32/6a/93d84463c00cecb561a7508aa6303e35ee2894294eac14245526924415fe/hf_xet-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9929561f5abf4581c8ea79587881dfef6b8abb2a0d8a51915936fc2a614f4e73", size = 3792731, upload-time = "2026-05-06T06:18:00.021Z" }, + { url = "https://files.pythonhosted.org/packages/9d/5a/8ec8e0c863b382d00b3c2e2af6ded6b06371be617144a625903a6d562f4b/hf_xet-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7b7bbae318e583a86fb21e5a4a175d6721d628a2874f4bd022d0e660c32a682", size = 4456738, upload-time = "2026-05-06T06:17:49.574Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ca/f7effa1a67717da2bcc6b6c28f71c6ca648c77acaec4e2c32f40cbe16d85/hf_xet-1.5.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cf7b2dc6f31a4ea754bb50f74cde482dcf5d366d184076d8530b9872787f3761", size = 4251622, upload-time = "2026-05-06T06:17:47.096Z" }, + { url = "https://files.pythonhosted.org/packages/65/f2/19247dba3e231cf77dec59ddfb878f00057635ff773d099c9b59d37812c3/hf_xet-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8dbcbab554c9ef158ef2c991545c3e970ddd8cc7acdcd0a78c5a41095dab4ded", size = 4445667, upload-time = "2026-05-06T06:18:11.983Z" }, + { url = "https://files.pythonhosted.org/packages/7f/64/6f116801a3bcfb6f59f5c251f48cadc47ea54026441c4a385079286a94fa/hf_xet-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5906bf7718d3636dc13402914736abe723492cb730f744834f5f5b67d3a12702", size = 4664619, upload-time = "2026-05-06T06:18:13.771Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e8/069542d37946ed08669b127e1496fa99e78196d71de8d41eda5e9f1b7a58/hf_xet-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5f3dc2248fc01cc0a00cd392ab497f1ca373fcbc7e3f2da1f452480b384e839e", size = 3966802, upload-time = "2026-05-06T06:18:28.162Z" }, + { url = "https://files.pythonhosted.org/packages/f9/91/fc6fdec27b14d04e88c386ac0a0129732b53fa23f7c4a78f4b83a039c567/hf_xet-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b285cea1b5bab46b758772716ba8d6854a1a0310fed1c249d678a8b38601e5a0", size = 3797168, upload-time = "2026-05-06T06:18:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/3d/fb/69ff198a82cae7eb1a69fb84d93b3a3e4816564d76817fe541ddc96874eb/hf_xet-1.5.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dad0dc84e941b8ba3c860659fe1fdc35c049d47cce293f003287757e971a8f56", size = 4030814, upload-time = "2026-05-06T06:17:57.933Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ff/edcc2b40162bef3ff78e14ab637e5f3b89243d6aee72f5949d3bb6a5af83/hf_xet-1.5.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fd6e5a9b0fdac4ed03ed45ef79254a655b1aaab514a02202617fbf643f5fdf7a", size = 3798444, upload-time = "2026-05-06T06:17:55.79Z" }, + { url = "https://files.pythonhosted.org/packages/49/4d/103f76b04310e5e57656696cc184690d20c466af0bca3ca88f8c8ea5d4f3/hf_xet-1.5.0-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3531b1823a0e6d77d80f9ed15ca0e00f0d115094f8ac033d5cae88f4564cc949", size = 4465986, upload-time = "2026-05-06T06:17:44.886Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a2/546f47f464737b3edbab6f8ddb57f2599b93d2cbb66f06abb475ccb48651/hf_xet-1.5.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9a0ee58cd18d5ea799f7ed11290bbccbe56bdd8b1d97ca74b9cc49a3945d7a3b", size = 4259865, upload-time = "2026-05-06T06:17:42.639Z" }, + { url = "https://files.pythonhosted.org/packages/95/7f/1be593c1f28613be2e196473481cd81bfc5910795e30a34e8f744f6cac4f/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e60df5a42e9bed8628b6416af2cba4cba57ae9f02de226a06b020d98e1aab18", size = 4459835, upload-time = "2026-05-06T06:18:08.026Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b2/703569fc881f3284487e68cda7b42179978480da3c438042a6bbbb4a671c/hf_xet-1.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4b35549ce62601b84da4ff9b24d970032ace3d4430f52d91bcbb26c901d6c690", size = 4672414, upload-time = "2026-05-06T06:18:09.864Z" }, + { url = "https://files.pythonhosted.org/packages/af/37/1b6def445c567286b50aa3b33828158e135b1be44938dde59f11382a500c/hf_xet-1.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:2806c7c17b4d23f8d88f7c4814f838c3b6150773fe339c20af23e1cfaf2797e4", size = 3977238, upload-time = "2026-05-06T06:18:23.621Z" }, + { url = "https://files.pythonhosted.org/packages/62/94/3b66b148778ee100dcfd69c2ca22b57b41b44d3063ceec934f209e9184ce/hf_xet-1.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:b6c9df403040248c76d808d3e047d64db2d923bae593eb244c41e425cf6cd7be", size = 3806916, upload-time = "2026-05-06T06:18:21.7Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httptools" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/88/1d21a36da8f5cb0fa49eafd4b169eba5608d57e75bbcf61845cbc6243216/httptools-0.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:880490234c10f70a9830743097e8958d6e4b9f5a0ffc24515023afeef984054d", size = 208247, upload-time = "2026-05-25T22:17:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/cc4feea2945cb3051038f090c9b36bd5b8a9d7f5a894a506a8983e33fd1c/httptools-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5931891fb7b441b8a3853cf1b85c82c903defce084dd5f6771ca46e31bf862c5", size = 113064, upload-time = "2026-05-25T22:17:09.136Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a6/febbb8b8db0f58b38e44ad6cb946e6a255ae49b55f2e8543408fb7501ccd/httptools-0.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2", size = 523851, upload-time = "2026-05-25T22:17:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/f90a0df0b83beff265b7e3b65f2a4cefd95792d4be0ac3e16049f2acd3c2/httptools-0.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:425f83884fd6343828d8c565f046cb72b6d19063f6924093e11bcd8e1548cd09", size = 518842, upload-time = "2026-05-25T22:17:11.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2d/0c9ac76dd2c893841fbf6498d6acec4f2442e1b7067f6e3e316a80e494e8/httptools-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7c3c97f4311c7be57e2986629df89d49cb434dbff78eafcd48c2bff986b15a", size = 501238, upload-time = "2026-05-25T22:17:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/ca/42/906adc91ae3a5fa9c59c0a2f21c139725bd7e5b41ae6acd485cd14123ebf/httptools-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1afd7c9fbff0d9f5d489c4ce2768bd09c84a46ddefc7161e6aa82ae35c85745", size = 509567, upload-time = "2026-05-25T22:17:13.842Z" }, + { url = "https://files.pythonhosted.org/packages/05/0b/4240efeb672751ee5b9b380cb0e3fdc050bc05f68adc7a8aefc4fcd9a69a/httptools-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd96f29b4bab1d42fa6e3d008711c75e0f79e94e06827330160e3a304227f150", size = 90918, upload-time = "2026-05-25T22:17:15.155Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148, upload-time = "2026-05-25T22:17:16.333Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368, upload-time = "2026-05-25T22:17:17.586Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447, upload-time = "2026-05-25T22:17:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448, upload-time = "2026-05-25T22:17:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460, upload-time = "2026-05-25T22:17:20.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312, upload-time = "2026-05-25T22:17:22.085Z" }, + { url = "https://files.pythonhosted.org/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117, upload-time = "2026-05-25T22:17:23.074Z" }, + { url = "https://files.pythonhosted.org/packages/1a/12/fa3fbf5f9517b273edea2dc982aa82a8c634091e67c590792b729017bc6f/httptools-0.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:de242a49b5d18e0a8776e654e9f6bf6d89f3875a5c35b425a0e7ce940feb3fd6", size = 206183, upload-time = "2026-05-25T22:17:24.004Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/5e7c4cb443370f2090a3aba0453a07384d29ff66b7435bb90e77e1037599/httptools-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:159e9ab5f701ccd42e555a12f1ad8ff69702910fc1c996cf2bb66e5fcb7a231b", size = 112079, upload-time = "2026-05-25T22:17:25.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/53/771bd891eb0f236f32145d6a1775777ec85745f3cc983a1f23d1a3b8ddfe/httptools-0.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4a9f1707e4823d54dfec6c33fa3697d302aed536ed352a7ebb5a061ddb869d0", size = 481596, upload-time = "2026-05-25T22:17:26.186Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/94e15bc68ce3d423243c45d7f1b0c7561f13844f97dc52ae23182fb65628/httptools-0.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d76ad7b951387e3632c8716a9bb03ac5b45c5f16119aa409db0459520887944e", size = 480865, upload-time = "2026-05-25T22:17:27.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/fe2980fc03723272e30f135b62360b075f513dfe7cc73aef36c7f04012bd/httptools-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a3b7387147361c3fd47a0bde763c5c91b5b4cd4dc9989b8ece84ff436c99843b", size = 463189, upload-time = "2026-05-25T22:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/47fc5fff68acd1bfa20b4734059c9a06cadb88119dcd5258b5b0d21d91c8/httptools-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f256d6ce930c52ca1cb2a960b7da03548c454e7d28b06059ad41bfe789036ce0", size = 466610, upload-time = "2026-05-25T22:17:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/07b13c93ffd9bec9546e0d43f8e19378dd696dbd278511406bc07371ef1f/httptools-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:19d1ee275bb59ba2643ba9a3a1e51cc0c788caf2b8df506368e03f56fdd08527", size = 92705, upload-time = "2026-05-25T22:17:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/121648f68ce066d7bd762d6b6d97e620847642d38d54f3d90ff11d947629/httptools-0.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:de1ed58a974e75d56560acc7e7fed01a454994429456f65209789992e41f2568", size = 215023, upload-time = "2026-05-25T22:17:32.401Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b0/312a062ae741ae3e8baa8c8bf20be81b2e67337b259ab4349bebc7b6142e/httptools-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e93c227b595c6926c1acee96891dd9da4be338cfbe82e5cd3bb9d8dd7dc4ac0b", size = 117405, upload-time = "2026-05-25T22:17:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/fc/37/fccd705f795386bb05bf413012fecff2a33e5aa8c2f069096de3e9fd8702/httptools-0.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2a021c3a8e65cc125390d72f59b968afca3bdcaff25bd67965e0a055a14946ca", size = 558497, upload-time = "2026-05-25T22:17:34.732Z" }, + { url = "https://files.pythonhosted.org/packages/bd/39/f172e8003576de35f5ba77ff417cf0e34429d35dc014deef15afa337a72c/httptools-0.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48774d39cbb70e2b1f71f88852a3087ae1d3a1eb80482bb48c13067ab080c14f", size = 571585, upload-time = "2026-05-25T22:17:35.813Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/f5564760af99f3dbbf3f9104dc00e5da27e96cf433c6bdcf77617f70bf3f/httptools-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:88eead8ec8680a9f146c655bc88445a325bd7921cfd8194c7337e9467282427d", size = 543297, upload-time = "2026-05-25T22:17:37.08Z" }, + { url = "https://files.pythonhosted.org/packages/99/67/8d9f2c313618e161b82f3873188e7196126da1d6e29688df40eb3997c77a/httptools-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c032fa028f46871ec7e1fc59fc15e8023eab3e6bbe6ece786a1611719a5d081", size = 539535, upload-time = "2026-05-25T22:17:38.032Z" }, + { url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/65/9826515abb600b5722bcf53f8b4a2fb58340b1f8bfcaee19f83561c13a44/huggingface_hub-1.17.0.tar.gz", hash = "sha256:fad842b6763ef70ebc3919665b1b9273645203185400a7d6c5eddc2323cc3435", size = 797082, upload-time = "2026-05-28T15:12:13.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/28/d7cef5e477b855c25d415b8f57e5bc7347c7a90cad3acf1725d0c92ca294/huggingface_hub-1.17.0-py3-none-any.whl", hash = "sha256:3b8156d23118e87f6a587648bfbc04f04a12a757ccb4ed298b35c4ae638bf24c", size = 671546, upload-time = "2026-05-28T15:12:11.441Z" }, +] + +[[package]] +name = "humming-kernels" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings" }, + { name = "jinja2" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "nvidia-ml-py" }, + { name = "pyelftools" }, + { name = "safetensors" }, + { name = "tabulate" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "triton" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/f4/e141f45697b7d0d38bfaf8766a7362d8f0136e3cff2620624f24f68e2700/humming_kernels-0.1.2.tar.gz", hash = "sha256:7894c80061c7866591bef12617da720ac4e925636ffc99464af433a5dcb035eb", size = 117251, upload-time = "2026-05-23T16:18:08.084Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/41/288bf756d921dbe98982eeb3ec4c20e7cb5224ea6dcb164f2df3d2f68a7f/humming_kernels-0.1.2-py3-none-any.whl", hash = "sha256:f7434b0424946445ef5ad5682bcabf309d97721818ed5bdc4c6f61de3c6b9d2f", size = 160951, upload-time = "2026-05-23T16:18:06.405Z" }, +] + +[package.optional-dependencies] +cu13 = [ + { name = "nvidia-cuda-cccl" }, + { name = "nvidia-cuda-nvcc" }, + { name = "nvidia-cuda-nvrtc", version = "13.0.88", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'darwin'" }, + { name = "nvidia-cuda-nvrtc", version = "13.3.33", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, + { name = "nvidia-cuda-runtime", version = "13.0.96", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'darwin'" }, + { name = "nvidia-cuda-runtime", version = "13.3.29", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, +] + +[[package]] +name = "idna" +version = "3.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, +] + +[[package]] +name = "ijson" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/57/60d1a6a512f2f0508d0bc8b4f1cc5616fd3196619b66bd6a01f9155a1292/ijson-3.5.0.tar.gz", hash = "sha256:94688760720e3f5212731b3cb8d30267f9a045fb38fb3870254e7b9504246f31", size = 68658, upload-time = "2026-02-24T03:58:30.974Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/17/9c63c7688025f3a8c47ea717b8306649c8c7244e49e20a2be4e3515dc75c/ijson-3.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ebefbe149a6106cc848a3eaf536af51a9b5ccc9082de801389f152dba6ab755", size = 88536, upload-time = "2026-02-24T03:57:06.809Z" }, + { url = "https://files.pythonhosted.org/packages/6f/dd/e15c2400244c117b06585452ebc63ae254f5a6964f712306afd1422daae0/ijson-3.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:19e30d9f00f82e64de689c0b8651b9cfed879c184b139d7e1ea5030cec401c21", size = 60499, upload-time = "2026-02-24T03:57:09.155Z" }, + { url = "https://files.pythonhosted.org/packages/77/a9/bf4fe3538a0c965f16b406f180a06105b875da83f0743e36246be64ef550/ijson-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a04a33ee78a6f27b9b8528c1ca3c207b1df3b8b867a4cf2fcc4109986f35c227", size = 60330, upload-time = "2026-02-24T03:57:10.574Z" }, + { url = "https://files.pythonhosted.org/packages/31/76/6f91bdb019dd978fce1bc5ea1cd620cfc096d258126c91db2c03a20a7f34/ijson-3.5.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7d48dc2984af02eb3c56edfb3f13b3f62f2f3e4fe36f058c8cfc75d93adf4fed", size = 138977, upload-time = "2026-02-24T03:57:11.932Z" }, + { url = "https://files.pythonhosted.org/packages/11/be/bbc983059e48a54b0121ee60042979faed7674490bbe7b2c41560db3f436/ijson-3.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1e73a44844d9adbca9cf2c4132cd875933e83f3d4b23881fcaf82be83644c7d", size = 149785, upload-time = "2026-02-24T03:57:13.255Z" }, + { url = "https://files.pythonhosted.org/packages/6d/81/2fee58f9024a3449aee83edfa7167fb5ccd7e1af2557300e28531bb68e16/ijson-3.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7389a56b8562a19948bdf1d7bae3a2edc8c7f86fb59834dcb1c4c722818e645a", size = 149729, upload-time = "2026-02-24T03:57:14.191Z" }, + { url = "https://files.pythonhosted.org/packages/c7/56/f1706761fcc096c9d414b3dcd000b1e6e5c24364c21cfba429837f98ee8d/ijson-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3176f23f8ebec83f374ed0c3b4e5a0c4db7ede54c005864efebbed46da123608", size = 150697, upload-time = "2026-02-24T03:57:15.855Z" }, + { url = "https://files.pythonhosted.org/packages/d9/6e/ee0d9c875a0193b632b3e9ccd1b22a50685fb510256ad57ba483b6529f77/ijson-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6babd88e508630c6ef86c9bebaaf13bb2fb8ec1d8f8868773a03c20253f599bc", size = 142873, upload-time = "2026-02-24T03:57:16.831Z" }, + { url = "https://files.pythonhosted.org/packages/d2/bf/f9d4399d0e6e3fd615035290a71e97c843f17f329b43638c0a01cf112d73/ijson-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dc1b3836b174b6db2fa8319f1926fb5445abd195dc963368092103f8579cb8ed", size = 151583, upload-time = "2026-02-24T03:57:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/71/a7254a065933c0e2ffd3586f46187d84830d3d7b6f41cfa5901820a4f87d/ijson-3.5.0-cp312-cp312-win32.whl", hash = "sha256:6673de9395fb9893c1c79a43becd8c8fbee0a250be6ea324bfd1487bb5e9ee4c", size = 53079, upload-time = "2026-02-24T03:57:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7b/2edca79b359fc9f95d774616867a03ecccdf333797baf5b3eea79733918c/ijson-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f4f7fabd653459dcb004175235f310435959b1bb5dfa8878578391c6cc9ad944", size = 55500, upload-time = "2026-02-24T03:57:20.428Z" }, + { url = "https://files.pythonhosted.org/packages/a2/71/d67e764a712c3590627480643a3b51efcc3afa4ef3cb54ee4c989073c97e/ijson-3.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e9cedc10e40dd6023c351ed8bfc7dcfce58204f15c321c3c1546b9c7b12562a4", size = 88544, upload-time = "2026-02-24T03:57:21.293Z" }, + { url = "https://files.pythonhosted.org/packages/1a/39/f1c299371686153fa3cf5c0736b96247a87a1bee1b7145e6d21f359c505a/ijson-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3647649f782ee06c97490b43680371186651f3f69bebe64c6083ee7615d185e5", size = 60495, upload-time = "2026-02-24T03:57:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/16/94/b1438e204d75e01541bebe3e668fe3e68612d210e9931ae1611062dd0a56/ijson-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90e74be1dce05fce73451c62d1118671f78f47c9f6be3991c82b91063bf01fc9", size = 60325, upload-time = "2026-02-24T03:57:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/30/e2/4aa9c116fa86cc8b0f574f3c3a47409edc1cd4face05d0e589a5a176b05d/ijson-3.5.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78e9ad73e7be2dd80627504bd5cbf512348c55ce2c06e362ed7683b5220e8568", size = 138774, upload-time = "2026-02-24T03:57:24.683Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d2/738b88752a70c3be1505faa4dcd7110668c2712e582a6a36488ed1e295d4/ijson-3.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9577449313cc94be89a4fe4b3e716c65f09cc19636d5a6b2861c4e80dddebd58", size = 149820, upload-time = "2026-02-24T03:57:26.062Z" }, + { url = "https://files.pythonhosted.org/packages/ed/df/0b3ab9f393ca8f72ea03bc896ba9fdc987e90ae08cdb51c32a4ee0c14d5e/ijson-3.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e4c1178fb50aff5f5701a30a5152ead82a14e189ce0f6102fa1b5f10b2f54ff", size = 149747, upload-time = "2026-02-24T03:57:27.308Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a3/b0037119f75131b78cb00acc2657b1a9d0435475f1f2c5f8f5a170b66b9c/ijson-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0eb402ab026ffb37a918d75af2b7260fe6cfbce13232cc83728a714dd30bd81d", size = 151027, upload-time = "2026-02-24T03:57:28.522Z" }, + { url = "https://files.pythonhosted.org/packages/22/a0/cb344de1862bf09d8f769c9d25c944078c87dd59a1b496feec5ad96309a4/ijson-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b08ee08355f9f729612a8eb9bf69cc14f9310c3b2a487c6f1c3c65d85216ec4", size = 142996, upload-time = "2026-02-24T03:57:29.774Z" }, + { url = "https://files.pythonhosted.org/packages/ca/32/a8ffd67182e02ea61f70f62daf43ded4fa8a830a2520a851d2782460aba8/ijson-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bda62b6d48442903e7bf56152108afb7f0f1293c2b9bef2f2c369defea76ab18", size = 152068, upload-time = "2026-02-24T03:57:30.969Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d1/3578df8e75d446aab0ae92e27f641341f586b85e1988536adebc65300cb4/ijson-3.5.0-cp313-cp313-win32.whl", hash = "sha256:8d073d9b13574cfa11083cc7267c238b7a6ed563c2661e79192da4a25f09c82c", size = 53065, upload-time = "2026-02-24T03:57:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a2/f7cdaf5896710da3e69e982e44f015a83d168aa0f3a89b6f074b5426779d/ijson-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:2419f9e32e0968a876b04d8f26aeac042abd16f582810b576936bbc4c6015069", size = 55499, upload-time = "2026-02-24T03:57:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/42/65/13e2492d17e19a2084523e18716dc2809159f2287fd2700c735f311e76c4/ijson-3.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4d4b0cd676b8c842f7648c1a783448fac5cd3b98289abd83711b3e275e143524", size = 93019, upload-time = "2026-02-24T03:57:33.976Z" }, + { url = "https://files.pythonhosted.org/packages/33/92/483fc97ece0c3f1cecabf48f6a7a36e89d19369eec462faaeaa34c788992/ijson-3.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:252dec3680a48bb82d475e36b4ae1b3a9d7eb690b951bb98a76c5fe519e30188", size = 62714, upload-time = "2026-02-24T03:57:34.819Z" }, + { url = "https://files.pythonhosted.org/packages/4b/88/793fe020a0fe9d9eed4c285cf4a5cfdb0a935708b3bde0d72f35c794b513/ijson-3.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:aa1b5dca97d323931fde2501172337384c958914d81a9dac7f00f0d4bfc76bc7", size = 62460, upload-time = "2026-02-24T03:57:35.874Z" }, + { url = "https://files.pythonhosted.org/packages/51/69/f1a2690aa8d4df1f4e262b385e65a933ffdc250b091531bac9a449c19e16/ijson-3.5.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7a5ec7fd86d606094bba6f6f8f87494897102fa4584ef653f3005c51a784c320", size = 199273, upload-time = "2026-02-24T03:57:37.07Z" }, + { url = "https://files.pythonhosted.org/packages/ea/a2/f1346d5299e79b988ab472dc773d5381ec2d57c23cb2f1af3ede4a810e62/ijson-3.5.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:009f41443e1521847701c6d87fa3923c0b1961be3c7e7de90947c8cb92ea7c44", size = 216884, upload-time = "2026-02-24T03:57:38.346Z" }, + { url = "https://files.pythonhosted.org/packages/28/3c/8b637e869be87799e6c2c3c275a30a546f086b1aed77e2b7f11512168c5a/ijson-3.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4c3651d1f9fe2839a93fdf8fd1d5ca3a54975349894249f3b1b572bcc4bd577", size = 207306, upload-time = "2026-02-24T03:57:39.718Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/18b1c1df6951ca056782d7580ec40cea4ff9a27a0947d92640d1cc8c4ae3/ijson-3.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:945b7abcfcfeae2cde17d8d900870f03536494245dda7ad4f8d056faa303256c", size = 211364, upload-time = "2026-02-24T03:57:40.953Z" }, + { url = "https://files.pythonhosted.org/packages/f3/55/e795812e82851574a9dba8a53fde045378f531ef14110c6fb55dbd23b443/ijson-3.5.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0574b0a841ff97495c13e9d7260fbf3d85358b061f540c52a123db9dbbaa2ed6", size = 200608, upload-time = "2026-02-24T03:57:42.272Z" }, + { url = "https://files.pythonhosted.org/packages/5c/cd/013c85b4749b57a4cb4c2670014d1b32b8db4ab1a7be92ea7aeb5d7fe7b5/ijson-3.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f969ffb2b89c5cdf686652d7fb66252bc72126fa54d416317411497276056a18", size = 205127, upload-time = "2026-02-24T03:57:43.286Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7c/faf643733e3ab677f180018f6a855c4ef70b7c46540987424c563c959e42/ijson-3.5.0-cp313-cp313t-win32.whl", hash = "sha256:59d3f9f46deed1332ad669518b8099920512a78bda64c1f021fcd2aff2b36693", size = 55282, upload-time = "2026-02-24T03:57:44.353Z" }, + { url = "https://files.pythonhosted.org/packages/69/22/94ddb47c24b491377aca06cd8fc9202cad6ab50619842457d2beefde21ea/ijson-3.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c2839fa233746d8aad3b8cd2354e441613f5df66d721d59da4a09394bd1db2b", size = 58016, upload-time = "2026-02-24T03:57:45.237Z" }, + { url = "https://files.pythonhosted.org/packages/7a/93/0868efe753dc1df80cc405cf0c1f2527a6991643607c741bff8dcb899b3b/ijson-3.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:25a5a6b2045c90bb83061df27cfa43572afa43ba9408611d7bfe237c20a731a9", size = 89094, upload-time = "2026-02-24T03:57:46.115Z" }, + { url = "https://files.pythonhosted.org/packages/24/94/fd5a832a0df52ef5e4e740f14ac8640725d61034a1b0c561e8b5fb424706/ijson-3.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8976c54c0b864bc82b951bae06567566ac77ef63b90a773a69cd73aab47f4f4f", size = 60715, upload-time = "2026-02-24T03:57:47.552Z" }, + { url = "https://files.pythonhosted.org/packages/70/79/1b9a90af5732491f9eec751ee211b86b11011e1158c555c06576d52c3919/ijson-3.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:859eb2038f7f1b0664df4241957694cc35e6295992d71c98659b22c69b3cbc10", size = 60638, upload-time = "2026-02-24T03:57:48.428Z" }, + { url = "https://files.pythonhosted.org/packages/23/6f/2c551ea980fe56f68710a8d5389cfbd015fc45aaafd17c3c52c346db6aa1/ijson-3.5.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c911aa02991c7c0d3639b6619b93a93210ff1e7f58bf7225d613abea10adc78e", size = 140667, upload-time = "2026-02-24T03:57:49.314Z" }, + { url = "https://files.pythonhosted.org/packages/25/0e/27b887879ba6a5bc29766e3c5af4942638c952220fd63e1e442674f7883a/ijson-3.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:903cbdc350173605220edc19796fbea9b2203c8b3951fb7335abfa8ed37afda8", size = 149850, upload-time = "2026-02-24T03:57:50.329Z" }, + { url = "https://files.pythonhosted.org/packages/da/1e/23e10e1bc04bf31193b21e2960dce14b17dbd5d0c62204e8401c59d62c08/ijson-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4549d96ded5b8efa71639b2160235415f6bdb8c83367615e2dbabcb72755c33", size = 149206, upload-time = "2026-02-24T03:57:51.261Z" }, + { url = "https://files.pythonhosted.org/packages/8e/90/e552f6495063b235cf7fa2c592f6597c057077195e517b842a0374fd470c/ijson-3.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6b2dcf6349e6042d83f3f8c39ce84823cf7577eba25bac5aae5e39bbbbbe9c1c", size = 150438, upload-time = "2026-02-24T03:57:52.198Z" }, + { url = "https://files.pythonhosted.org/packages/5c/18/45bf8f297c41b42a1c231d261141097babd953d2c28a07be57ae4c3a1a02/ijson-3.5.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e44af39e6f8a17e5627dcd89715d8279bf3474153ff99aae031a936e5c5572e5", size = 144369, upload-time = "2026-02-24T03:57:53.22Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/deb9772bb2c0cead7ad64f00c3598eec9072bdf511818e70e2c512eeabbe/ijson-3.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9260332304b7e7828db56d43f08fc970a3ab741bf84ff10189361ea1b60c395b", size = 151352, upload-time = "2026-02-24T03:57:54.375Z" }, + { url = "https://files.pythonhosted.org/packages/e4/51/67f4d80cd58ad7eab0cd1af5fe28b961886338956b2f88c0979e21914346/ijson-3.5.0-cp314-cp314-win32.whl", hash = "sha256:63bc8121bb422f6969ced270173a3fa692c29d4ae30c860a2309941abd81012a", size = 53610, upload-time = "2026-02-24T03:57:55.655Z" }, + { url = "https://files.pythonhosted.org/packages/70/d3/263672ea22983ba3940f1534316dbc9200952c1c2a2332d7a664e4eaa7ae/ijson-3.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:01b6dad72b7b7df225ef970d334556dfad46c696a2c6767fb5d9ed8889728bca", size = 56301, upload-time = "2026-02-24T03:57:56.584Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d9/86f7fac35e0835faa188085ae0579e813493d5261ce056484015ad533445/ijson-3.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:2ea4b676ec98e374c1df400a47929859e4fa1239274339024df4716e802aa7e4", size = 93069, upload-time = "2026-02-24T03:57:57.849Z" }, + { url = "https://files.pythonhosted.org/packages/33/d2/e7366ed9c6e60228d35baf4404bac01a126e7775ea8ce57f560125ed190a/ijson-3.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:014586eec043e23c80be9a923c56c3a0920a0f1f7d17478ce7bc20ba443968ef", size = 62767, upload-time = "2026-02-24T03:57:58.758Z" }, + { url = "https://files.pythonhosted.org/packages/35/8b/3e703e8cc4b3ada79f13b28070b51d9550c578f76d1968657905857b2ddd/ijson-3.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5b8b886b0248652d437f66e7c5ac318bbdcb2c7137a7e5327a68ca00b286f5f", size = 62467, upload-time = "2026-02-24T03:58:00.261Z" }, + { url = "https://files.pythonhosted.org/packages/21/42/0c91af32c1ee8a957fdac2e051b5780756d05fd34e4b60d94a08d51bac1d/ijson-3.5.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:498fd46ae2349297e43acf97cdc421e711dbd7198418677259393d2acdc62d78", size = 200447, upload-time = "2026-02-24T03:58:01.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/80/796ea0e391b7e2d45c5b1b451734bba03f81c2984cf955ea5eaa6c4920ad/ijson-3.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22a51b4f9b81f12793731cf226266d1de2112c3c04ba4a04117ad4e466897e05", size = 217820, upload-time = "2026-02-24T03:58:02.598Z" }, + { url = "https://files.pythonhosted.org/packages/38/14/52b6613fdda4078c62eb5b4fe3efc724ddc55a4ad524c93de51830107aa3/ijson-3.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9636c710dc4ac4a281baa266a64f323b4cc165cec26836af702c44328b59a515", size = 208310, upload-time = "2026-02-24T03:58:04.759Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ad/8b3105a78774fd4a65e534a21d975ef3a77e189489fe3029ebcaeba5e243/ijson-3.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f7168a39e8211107666d71b25693fd1b2bac0b33735ef744114c403c6cac21e1", size = 211843, upload-time = "2026-02-24T03:58:05.836Z" }, + { url = "https://files.pythonhosted.org/packages/36/ab/a2739f6072d6e1160581bc3ed32da614c8cced023dcd519d9c5fa66e0425/ijson-3.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8696454245415bc617ab03b0dc3ae4c86987df5dc6a90bad378fe72c5409d89e", size = 200906, upload-time = "2026-02-24T03:58:07.788Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5e/e06c2de3c3d4a9cfb655c1ad08a68fb72838d271072cdd3196576ac4431a/ijson-3.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c21bfb61f71f191565885bf1bc29e0a186292d866b4880637b833848360bdc1b", size = 205495, upload-time = "2026-02-24T03:58:09.163Z" }, + { url = "https://files.pythonhosted.org/packages/7c/11/778201eb2e202ddd76b36b0fb29bf3d8e3c167389d8aa883c62524e49f47/ijson-3.5.0-cp314-cp314t-win32.whl", hash = "sha256:a2619460d6795b70d0155e5bf016200ac8a63ab5397aa33588bb02b6c21759e6", size = 56280, upload-time = "2026-02-24T03:58:10.116Z" }, + { url = "https://files.pythonhosted.org/packages/23/28/96711503245339084c8086b892c47415895eba49782d6cc52d9f4ee50301/ijson-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4f24b78d4ef028d17eb57ad1b16c0aed4a17bdd9badbf232dc5d9305b7e13854", size = 58965, upload-time = "2026-02-24T03:58:11.278Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "interegular" +version = "0.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/9d/8b6dde58a028a3962ce17e84d5fe73758df61378e00ef8ac3d85da34b0ff/interegular-0.3.3.tar.gz", hash = "sha256:d9b697b21b34884711399ba0f0376914b81899ce670032486d0d048344a76600", size = 24705, upload-time = "2024-01-06T23:01:22.372Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/01/72d6472f80651673716d1deda2a5bbb633e563ecf94f4479da5519d69d25/interegular-0.3.3-py37-none-any.whl", hash = "sha256:b0c07007d48c89d6d19f7204972d369b2a77222722e126b6aa63aa721dc3b19c", size = 23635, upload-time = "2024-01-06T23:01:20.829Z" }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, +] + +[[package]] +name = "jaraco-context" +version = "6.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272, upload-time = "2026-05-15T21:34:10.025Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594, upload-time = "2026-05-15T21:34:08.595Z" }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jiter" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" }, + { url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" }, + { url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" }, + { url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" }, + { url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" }, + { url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" }, + { url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" }, + { url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" }, + { url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" }, + { url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" }, + { url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" }, + { url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" }, + { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" }, + { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" }, + { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" }, + { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" }, + { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" }, + { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" }, + { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/079f350ebf7859d081de30aa890f9e3be68516f754f3ba32366ffff4dcee/jiter-0.15.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49", size = 308884, upload-time = "2026-05-19T10:08:31.667Z" }, + { url = "https://files.pythonhosted.org/packages/04/4e/a2c30a7f69b48c03b20935d647479106fe932f6e63f75faf53937197e05d/jiter-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86", size = 310028, upload-time = "2026-05-19T10:08:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/2e7cdfd3cf8ca967be38c48f5cf474d79f089efaf559a40f15984a77ae69/jiter-0.15.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f", size = 337485, upload-time = "2026-05-19T10:08:35.259Z" }, + { url = "https://files.pythonhosted.org/packages/9b/11/15a1aa28b120b8ee5b4f1fb894c125046225f09847738bd64233d3b84883/jiter-0.15.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e", size = 364223, upload-time = "2026-05-19T10:08:36.694Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/f442e8af5f3d0dcf47b39e83a0efd9ee45ea946aa6d04625dc3181eae3b6/jiter-0.15.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6", size = 456387, upload-time = "2026-05-19T10:08:38.143Z" }, + { url = "https://files.pythonhosted.org/packages/da/f4/37f2d2c9f64f49af7da652ed7532bb5a2372e588e6927c3fdd76f911db65/jiter-0.15.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9", size = 374461, upload-time = "2026-05-19T10:08:39.869Z" }, + { url = "https://files.pythonhosted.org/packages/60/28/edcfbbbf0cb15436f36664a8908a0df47ab9006298d4cd937dc08ea932d6/jiter-0.15.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c", size = 345924, upload-time = "2026-05-19T10:08:41.668Z" }, + { url = "https://files.pythonhosted.org/packages/47/13/89fba6398dab7f202b7278c4b4aac122399d2c0183971c4a57a3b7088df5/jiter-0.15.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd", size = 352283, upload-time = "2026-05-19T10:08:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/1b/da/0f6af8cef2c565a1ab44d970f268c43ccaa72707386ea6388e6fe2b6cd26/jiter-0.15.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89", size = 389985, upload-time = "2026-05-19T10:08:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ec/b9cb7d6d29e24ee14910266157d2a279d7a8f60ee0df7fa840882976ba64/jiter-0.15.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554", size = 517695, upload-time = "2026-05-19T10:08:46.486Z" }, + { url = "https://files.pythonhosted.org/packages/64/5e/6d1bda880723aae0ad86b4b763f044362448efe31e3e819635d41cb03451/jiter-0.15.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a", size = 548868, upload-time = "2026-05-19T10:08:48.026Z" }, + { url = "https://files.pythonhosted.org/packages/0c/72/7de501cf38dcacaf35098796f3a50e0f2e338baba18a58946c618544b809/jiter-0.15.0-cp314-cp314-win32.whl", hash = "sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec", size = 206380, upload-time = "2026-05-19T10:08:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a9/e19addf4b0c1bdce52c6da12351e6bc42c340c45e7c09e2158e46d293ccc/jiter-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558", size = 197687, upload-time = "2026-05-19T10:08:51.088Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c9/776b1db01db25fc6c1d58d1979a37b0a9fe787e5f5b1d062d2eaacb77923/jiter-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866", size = 192571, upload-time = "2026-05-19T10:08:52.451Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f6/45bb4670bacf300fd2c7abadbfb3af376e5f1b6ae75fd9bc069891d15870/jiter-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d", size = 317151, upload-time = "2026-05-19T10:08:53.867Z" }, + { url = "https://files.pythonhosted.org/packages/d7/68/ed635ad5acd7b73e454283083bbb7c8205ad10e88b0d9d7d793b09fe8226/jiter-0.15.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6", size = 341243, upload-time = "2026-05-19T10:08:55.383Z" }, + { url = "https://files.pythonhosted.org/packages/5d/db/3ff4176b817b8ea33879e71e13d8bc2b0d481a7ed3fe9e080f333d415c16/jiter-0.15.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995", size = 363629, upload-time = "2026-05-19T10:08:56.928Z" }, + { url = "https://files.pythonhosted.org/packages/ab/24/5f8270e0ba9c883582f96f722f8a0b58015c7ce1f8c6d4571cf394e99b6b/jiter-0.15.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8", size = 456198, upload-time = "2026-05-19T10:08:58.618Z" }, + { url = "https://files.pythonhosted.org/packages/45/5b/76fc02b0b5c54c3d18c60653156e2f76fde1816f9b4722db68d6ee2c897e/jiter-0.15.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5", size = 373710, upload-time = "2026-05-19T10:09:00.151Z" }, + { url = "https://files.pythonhosted.org/packages/c4/52/4310821b0ea9277994d3e1f49fc6a4b34e4800caebacb2c0af81da59a454/jiter-0.15.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b", size = 349901, upload-time = "2026-05-19T10:09:01.621Z" }, + { url = "https://files.pythonhosted.org/packages/93/fe/67648c35b3594fba8854ac64cc8a826d8bcd18324bbdb53d77697c60b6ef/jiter-0.15.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8", size = 352438, upload-time = "2026-05-19T10:09:03.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/0a1879d07ad6b3e025a2750027363452ced93c2d16d1c9d4b153ffd51c91/jiter-0.15.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec", size = 388152, upload-time = "2026-05-19T10:09:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/c1/78/46c6f6b56ba85c90021f4afd72ed42f691f8f84daacb5fe27277070e3858/jiter-0.15.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e", size = 517707, upload-time = "2026-05-19T10:09:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/ca/cb/720662d4c88fcad606e826fef5424365527ba43ce4868a479aed8f8c507e/jiter-0.15.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5", size = 548241, upload-time = "2026-05-19T10:09:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/60/e3/935b8034fd143f21125c87d51404a9e0e1449186a494405721ff5d1d695e/jiter-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52", size = 207950, upload-time = "2026-05-19T10:09:09.616Z" }, + { url = "https://files.pythonhosted.org/packages/93/59/984fd9ece895953dad3e0880a650e766f5a2da2c5514f0eafdaaabbeb5f9/jiter-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854", size = 200055, upload-time = "2026-05-19T10:09:11.367Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/cf8d779feb133a27a2e3bc833bccb9e13aa332cdf820497ebf72c10ce8c3/jiter-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0", size = 191244, upload-time = "2026-05-19T10:09:12.74Z" }, + { url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "joserfc" +version = "1.6.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/ac/d4fd5b30f82900eac60d765f179f0ba005825ac462cc8ced6e13ec685ab3/joserfc-1.6.8.tar.gz", hash = "sha256:878620c553a6ebdd76ccdc356782fee3f735f21a356d079a546b42a4670ace5f", size = 232930, upload-time = "2026-05-27T03:22:37.819Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/8c/5cdce2cf3ce8155849baf9a5e2ce77e89dc87ec3bdb38259e5d85fbc45bd/joserfc-1.6.8-py3-none-any.whl", hash = "sha256:22fb31a69094a5e6f44632002a9df2c30c941fc6c8ce1b037e92c03de954cf9f", size = 70927, upload-time = "2026-05-27T03:22:35.796Z" }, +] + +[[package]] +name = "jsonref" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-path" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "pathable" }, + { name = "pyyaml" }, + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/79/cd02a4df6d9270efdc7d3feefe6edd730b0820c39eeaa107a2faee8322d5/jsonschema_path-0.5.0.tar.gz", hash = "sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c", size = 19597, upload-time = "2026-05-19T20:45:00.971Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/2c/9e69d73c4297508be9e3b64a970ea3971b3eb8db64ffc5802d40bd25981f/jsonschema_path-0.5.0-py3-none-any.whl", hash = "sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2", size = 24077, upload-time = "2026-05-19T20:44:59.225Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "keyring" +version = "25.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, +] + +[[package]] +name = "lark" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80", size = 252132, upload-time = "2024-08-13T19:49:00.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size = 111036, upload-time = "2024-08-13T19:48:58.603Z" }, +] + +[[package]] +name = "llguidance" +version = "1.7.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/2a/e889d6fdddda852171cf537486513d59fd8d9c38104323c1851a73675f1f/llguidance-1.7.5.tar.gz", hash = "sha256:afaa8f979708cd546c762f06a4fe4748e5ef7f06ed45875dabe7db8f07b73645", size = 1156674, upload-time = "2026-04-29T19:11:09.915Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/fa/1b13a1d77998df4feca2e4c47e46d298050f60d6436e0e99fbd952b5e9fd/llguidance-1.7.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ea659e51f57b28af93a742f748f65be92264bdcf6bdab413b08d7a539e33550a", size = 3214888, upload-time = "2026-04-29T19:10:44.519Z" }, + { url = "https://files.pythonhosted.org/packages/04/10/43190ae55cad6d36d0dba599966b58f78acffca2f7aac63b05f67684dcbe/llguidance-1.7.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9e7b181638f7b473d32e0ce6a1cac25a470f06de65e70e2c6fb2fde283047762", size = 3129767, upload-time = "2026-04-29T19:10:46.366Z" }, + { url = "https://files.pythonhosted.org/packages/6d/df/c8003f68c322e0476db341dac58d53bceec15a95d137f3a187637bf424a6/llguidance-1.7.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:289bce21879105ff26e30a6b37444de01ea09d05efc795ca4ed958097f14844c", size = 3471274, upload-time = "2026-04-29T19:10:48.161Z" }, + { url = "https://files.pythonhosted.org/packages/68/de/56a42638f9c17c39c701a21c577271df962df7a7b8c9903760a0af4455eb/llguidance-1.7.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b950423c5e6c9c9bc35af5f86739c8a71b719a1dfa6c82bf6e3c11badeb838df", size = 3489162, upload-time = "2026-04-29T19:10:51.719Z" }, + { url = "https://files.pythonhosted.org/packages/63/a3/e196571aa72f578a2a106ec29883cf11344480d7b4d2513acb348b30e4f6/llguidance-1.7.5-cp314-cp314t-win_amd64.whl", hash = "sha256:b3da6ceaa03739ff3418e7a97d456f47f6242aab328abf8ea5a0abf17cb49ffc", size = 2873031, upload-time = "2026-04-29T19:10:55.233Z" }, + { url = "https://files.pythonhosted.org/packages/d7/e7/5c019dcd5c0312bd7b2ddaa3563c630a87bc51bfa692aed60999d5ac2bc7/llguidance-1.7.5-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dd805b8b0302edfa18c9b2b4b9ecf7f7b23f5bea42a44a91e7706238ffd21cef", size = 3225139, upload-time = "2026-04-29T19:10:56.95Z" }, + { url = "https://files.pythonhosted.org/packages/32/93/ecbe86d090afe4de7ab74ddc93b03a6cef8b01c62e06fa87e462e2dc4ffc/llguidance-1.7.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:421ff50f59fbe21bc3cba509e02366312a0de050088d2754711d1f1edb5dfe2b", size = 3136321, upload-time = "2026-04-29T19:10:58.49Z" }, + { url = "https://files.pythonhosted.org/packages/fc/dc/97cff2071bd9f0659db30655cfeb10bceaed91f7dee3ecbe2c813bd43642/llguidance-1.7.5-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:c1dfda8d8c47da5be5e47b30084eadb2ef331ab08dc6e3a114429511ab13ae05", size = 2901942, upload-time = "2026-04-29T19:10:59.958Z" }, + { url = "https://files.pythonhosted.org/packages/27/c4/2b9b9d0de824a71627373b0ccdbcf61bd56133b52c3f5b988a803f55d2c0/llguidance-1.7.5-cp39-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:1d02dbc64dc1afc2d2cb7e5e868886527f8c6f088062e87d81bbad6212e22500", size = 3073011, upload-time = "2026-04-29T19:11:01.949Z" }, + { url = "https://files.pythonhosted.org/packages/96/10/81469d27185bec13720ff1eff4b07c3d157d5633d0b2522ffb11ae09e81b/llguidance-1.7.5-cp39-abi3-win_amd64.whl", hash = "sha256:502e55c4521dde4be352b34e508dc665ddea3ae1f73c55c869f2d8b63475e4e5", size = 2883265, upload-time = "2026-04-29T19:11:08.414Z" }, +] + +[[package]] +name = "llvmlite" +version = "0.47.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614, upload-time = "2026-03-31T18:29:53.497Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/48/4b7fe0e34c169fa2f12532916133e0b219d2823b540733651b34fdac509a/llvmlite-0.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f", size = 37232769, upload-time = "2026-03-31T18:28:43.735Z" }, + { url = "https://files.pythonhosted.org/packages/e6/4b/e3f2cd17822cf772a4a51a0a8080b0032e6d37b2dbe8cfb724eac4e31c52/llvmlite-0.47.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5853bf26160857c0c2573415ff4efe01c4c651e59e2c55c2a088740acfee51cd", size = 56275178, upload-time = "2026-03-31T18:28:48.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/55/a3b4a543185305a9bdf3d9759d53646ed96e55e7dfd43f53e7a421b8fbae/llvmlite-0.47.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:003bcf7fa579e14db59c1a1e113f93ab8a06b56a4be31c7f08264d1d4072d077", size = 55128632, upload-time = "2026-03-31T18:28:52.901Z" }, + { url = "https://files.pythonhosted.org/packages/2f/f5/d281ae0f79378a5a91f308ea9fdb9f9cc068fddd09629edc0725a5a8fde1/llvmlite-0.47.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3079f25bdc24cd9d27c4b2b5e68f5f60c4fdb7e8ad5ee2b9b006007558f9df7", size = 38138692, upload-time = "2026-03-31T18:28:57.147Z" }, + { url = "https://files.pythonhosted.org/packages/77/6f/4615353e016799f80fa52ccb270a843c413b22361fadda2589b2922fb9b0/llvmlite-0.47.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a3c6a735d4e1041808434f9d440faa3d78d9b4af2ee64d05a66f351883b6ceec", size = 37232771, upload-time = "2026-03-31T18:29:01.324Z" }, + { url = "https://files.pythonhosted.org/packages/31/b8/69f5565f1a280d032525878a86511eebed0645818492feeb169dfb20ae8e/llvmlite-0.47.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2699a74321189e812d476a43d6d7f652f51811e7b5aad9d9bba842a1c7927acb", size = 56275178, upload-time = "2026-03-31T18:29:05.748Z" }, + { url = "https://files.pythonhosted.org/packages/d6/da/b32cafcb926fb0ce2aa25553bf32cb8764af31438f40e2481df08884c947/llvmlite-0.47.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c6951e2b29930227963e53ee152441f0e14be92e9d4231852102d986c761e40", size = 55128632, upload-time = "2026-03-31T18:29:11.235Z" }, + { url = "https://files.pythonhosted.org/packages/46/9f/4898b44e4042c60fafcb1162dfb7014f6f15b1ec19bf29cfea6bf26df90d/llvmlite-0.47.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2e9adf8698d813a9a5efb2d4370caf344dbc1e145019851fee6a6f319ba760e", size = 38138695, upload-time = "2026-03-31T18:29:15.43Z" }, + { url = "https://files.pythonhosted.org/packages/1c/d4/33c8af00f0bf6f552d74f3a054f648af2c5bc6bece97972f3bfadce4f5ec/llvmlite-0.47.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:de966c626c35c9dff5ae7bf12db25637738d0df83fc370cf793bc94d43d92d14", size = 37232773, upload-time = "2026-03-31T18:29:19.453Z" }, + { url = "https://files.pythonhosted.org/packages/64/1d/a760e993e0c0ba6db38d46b9f48f6c7dceb8ac838824997fb9e25f97bc04/llvmlite-0.47.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ddbccff2aeaff8670368340a158abefc032fe9b3ccf7d9c496639263d00151aa", size = 56275176, upload-time = "2026-03-31T18:29:24.149Z" }, + { url = "https://files.pythonhosted.org/packages/84/3b/e679bc3b29127182a7f4aa2d2e9e5bea42adb93fb840484147d59c236299/llvmlite-0.47.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4a7b778a2e144fc64468fb9bf509ac1226c9813a00b4d7afea5d988c4e22fca", size = 55128631, upload-time = "2026-03-31T18:29:29.536Z" }, + { url = "https://files.pythonhosted.org/packages/be/f7/19e2a09c62809c9e63bbd14ce71fb92c6ff7b7b3045741bb00c781efc3c9/llvmlite-0.47.0-cp314-cp314-win_amd64.whl", hash = "sha256:694e3c2cdc472ed2bd8bd4555ca002eec4310961dd58ef791d508f57b5cc4c94", size = 39153826, upload-time = "2026-03-31T18:29:33.681Z" }, + { url = "https://files.pythonhosted.org/packages/40/a1/581a8c707b5e80efdbbe1dd94527404d33fe50bceb71f39d5a7e11bd57b7/llvmlite-0.47.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:92ec8a169a20b473c1c54d4695e371bde36489fc1efa3688e11e99beba0abf9c", size = 37232772, upload-time = "2026-03-31T18:29:37.952Z" }, + { url = "https://files.pythonhosted.org/packages/11/03/16090dd6f74ba2b8b922276047f15962fbeea0a75d5601607edb301ba945/llvmlite-0.47.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa1cbd800edd3b20bc141521f7fd45a6185a5b84109aa6855134e81397ffe72b", size = 56275178, upload-time = "2026-03-31T18:29:42.58Z" }, + { url = "https://files.pythonhosted.org/packages/f5/cb/0abf1dd4c5286a95ffe0c1d8c67aec06b515894a0dd2ac97f5e27b82ab0b/llvmlite-0.47.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6725179b89f03b17dabe236ff3422cb8291b4c1bf40af152826dfd34e350ae8", size = 55128632, upload-time = "2026-03-31T18:29:46.939Z" }, + { url = "https://files.pythonhosted.org/packages/4f/79/d3bbab197e86e0ff4f9c07122895b66a3e0d024247fcff7f12c473cb36d9/llvmlite-0.47.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6842cf6f707ec4be3d985a385ad03f72b2d724439e118fcbe99b2929964f0453", size = 39153839, upload-time = "2026-03-31T18:29:51.004Z" }, +] + +[[package]] +name = "lm-format-enforcer" +version = "0.11.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "interegular" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/d5/41cd417ba7dfdbbcfe46cebf81fb3dfd7c591b89897560ad05bb410a465d/lm_format_enforcer-0.11.3.tar.gz", hash = "sha256:e68081c108719cce284a9bcc889709b26ffb085a1945b5eba3a12cfa96d528da", size = 40258, upload-time = "2025-08-24T19:37:47.527Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/ef/11292bb0b85cf4c93447cab5a29f64576ed14d3ab4280e35ddd23486594a/lm_format_enforcer-0.11.3-py3-none-any.whl", hash = "sha256:cf586350875def1ae7a8fba84fcbbfc8371424b6c9d05c1fcba70aa233fbf06f", size = 45418, upload-time = "2025-08-24T19:37:46.325Z" }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mcp" +version = "1.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/3c/347cf965d313f5d41764e7d46bea6ffe7d9ef13b983cc429b0340962a082/mcp-1.27.2.tar.gz", hash = "sha256:8e02db104096d1c25b28e64bde29a5c32b31bc241710213e12fd4d84985bdfef", size = 621116, upload-time = "2026-05-29T17:16:04.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/11/252c6f971dc4f16af1d98a1c469d8ba523aab00d1bb76b4d3bc1ff32eacc/mcp-1.27.2-py3-none-any.whl", hash = "sha256:d6ff5160c6ca65d93013626efb3fc249de683c30b2d8570755ceddd490344de5", size = 220498, upload-time = "2026-05-29T17:16:02.442Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mini-swe-train" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "accelerate" }, + { name = "aiohttp" }, + { name = "bitsandbytes" }, + { name = "datasets" }, + { name = "fastmcp" }, + { name = "flash-linear-attention" }, + { name = "hf-sandbox" }, + { name = "huggingface-hub" }, + { name = "openenv-core" }, + { name = "openenv-mini-swe-env" }, + { name = "peft" }, + { name = "requests" }, + { name = "trackio" }, + { name = "transformers" }, + { name = "trl" }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] +vllm = [ + { name = "vllm" }, +] + +[package.metadata] +requires-dist = [ + { name = "accelerate", specifier = ">=1.13.0" }, + { name = "aiohttp", specifier = ">=3.13.5" }, + { name = "bitsandbytes", specifier = ">=0.43.0" }, + { name = "datasets", specifier = ">=4.8.0" }, + { name = "fastmcp", specifier = ">=3.3.0" }, + { name = "flash-linear-attention", specifier = ">=0.5.0" }, + { name = "hf-sandbox", git = "https://github.com/rycerzes/hf-sandbox?rev=feat%2Fnamed-tunnels" }, + { name = "huggingface-hub", specifier = ">=1.5" }, + { name = "openenv-core", editable = "../../" }, + { name = "openenv-mini-swe-env", editable = "../../envs/mini_swe_env" }, + { name = "peft", specifier = ">=0.15.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, + { name = "requests", specifier = ">=2.25.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.14.0" }, + { name = "trackio", specifier = ">=0.25.0" }, + { name = "transformers", specifier = ">=5.2.0" }, + { name = "trl", git = "https://github.com/rycerzes/trl?rev=feat%2Fasync-grpo-parity" }, + { name = "vllm", marker = "extra == 'vllm'", specifier = ">=0.21.0" }, +] +provides-extras = ["vllm", "dev"] + +[[package]] +name = "mistral-common" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "pydantic-extra-types", extra = ["pycountry"] }, + { name = "requests" }, + { name = "tiktoken" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/eb/12167a1bea9714582e5b4f539f9c019323363e314a499c72855ff0e5ad43/mistral_common-1.11.2.tar.gz", hash = "sha256:79f68fc2d1190f28637f40e053f919c8c2697e00b2aa679ddee562a95183f4ad", size = 6357845, upload-time = "2026-05-04T19:47:40.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/f0/6a5d604b972e442b9d36c117d01788feddad099e4965699e3516ee6fefc3/mistral_common-1.11.2-py3-none-any.whl", hash = "sha256:ebb42062cd705a0aa2bc69b4cde2b83d446ae58150b7e29322c90cb08fcfca6c", size = 6531968, upload-time = "2026-05-04T19:47:37.718Z" }, +] + +[package.optional-dependencies] +image = [ + { name = "opencv-python-headless" }, +] + +[[package]] +name = "ml-dtypes" +version = "0.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac", size = 676927, upload-time = "2025-11-17T22:31:48.182Z" }, + { url = "https://files.pythonhosted.org/packages/54/0f/428ef6881782e5ebb7eca459689448c0394fa0a80bea3aa9262cba5445ea/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900", size = 5028464, upload-time = "2025-11-17T22:31:50.135Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cb/28ce52eb94390dda42599c98ea0204d74799e4d8047a0eb559b6fd648056/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff", size = 5009002, upload-time = "2025-11-17T22:31:52.001Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f0/0cfadd537c5470378b1b32bd859cf2824972174b51b873c9d95cfd7475a5/ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7", size = 212222, upload-time = "2025-11-17T22:31:53.742Z" }, + { url = "https://files.pythonhosted.org/packages/16/2e/9acc86985bfad8f2c2d30291b27cd2bb4c74cea08695bd540906ed744249/ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460", size = 160793, upload-time = "2025-11-17T22:31:55.358Z" }, + { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888, upload-time = "2025-11-17T22:31:56.907Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993, upload-time = "2025-11-17T22:31:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956, upload-time = "2025-11-17T22:31:59.931Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224, upload-time = "2025-11-17T22:32:01.349Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798, upload-time = "2025-11-17T22:32:02.864Z" }, + { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083, upload-time = "2025-11-17T22:32:04.08Z" }, + { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111, upload-time = "2025-11-17T22:32:05.546Z" }, + { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453, upload-time = "2025-11-17T22:32:07.115Z" }, + { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612, upload-time = "2025-11-17T22:32:08.615Z" }, + { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145, upload-time = "2025-11-17T22:32:09.782Z" }, + { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781, upload-time = "2025-11-17T22:32:11.364Z" }, + { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145, upload-time = "2025-11-17T22:32:12.783Z" }, + { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230, upload-time = "2025-11-17T22:32:14.38Z" }, + { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032, upload-time = "2025-11-17T22:32:15.763Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353, upload-time = "2025-11-17T22:32:16.932Z" }, + { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085, upload-time = "2025-11-17T22:32:18.175Z" }, + { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358, upload-time = "2025-11-17T22:32:19.7Z" }, + { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332, upload-time = "2025-11-17T22:32:21.193Z" }, + { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612, upload-time = "2025-11-17T22:32:22.579Z" }, + { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825, upload-time = "2025-11-17T22:32:23.766Z" }, +] + +[[package]] +name = "model-hosting-container-standards" +version = "0.1.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastapi" }, + { name = "httpx" }, + { name = "jmespath" }, + { name = "pydantic" }, + { name = "setuptools" }, + { name = "starlette" }, + { name = "supervisor" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/5a/d669bdeb5ba96db42c6ef010835a25119b05f8c35ee5f1c3f715626625fe/model_hosting_container_standards-0.1.15.tar.gz", hash = "sha256:ae8dd74d3250545c14f0a7068186c7b0f0ab6563d31e7137f556b6b660c8a6a9", size = 93994, upload-time = "2026-05-05T18:22:29.357Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/26/c7aea197f1719f31d0dd686eb4475982fe9efd7668ce259cb52b62c676b6/model_hosting_container_standards-0.1.15-py3-none-any.whl", hash = "sha256:849e08c4732203ee861c8c24966b4e916ea4420fa324b430f7f74a1e1fe8811a", size = 125418, upload-time = "2026-05-05T18:22:27.819Z" }, +] + +[[package]] +name = "more-itertools" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772, upload-time = "2026-05-22T14:14:29.909Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "msgspec" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/60/f79b9b013a16fa3a58350c9295ddc6789f2e335f36ea61ed10a21b215364/msgspec-0.21.1.tar.gz", hash = "sha256:2313508e394b0d208f8f56892ca9b2799e2561329de9763b19619595a6c0f72c", size = 319193, upload-time = "2026-04-12T21:44:50.394Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/cf/317224852c00248c620a9bcf4b26e2e4ab8afd752f18d2a6ef73ebd423b6/msgspec-0.21.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4248cf0b6129b7d230eacd493c17cc2d4f3989f3bb7f633a928a85b7dcfa251", size = 196188, upload-time = "2026-04-12T21:44:07.181Z" }, + { url = "https://files.pythonhosted.org/packages/6d/81/074612945c0666078f7366f40000013de9f6ba687491d450df699bceebc9/msgspec-0.21.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5102c7e9b3acff82178449b85006d96310e690291bb1ea0142f1b24bcb8aabcb", size = 188473, upload-time = "2026-04-12T21:44:08.736Z" }, + { url = "https://files.pythonhosted.org/packages/8a/37/655101799590bcc5fddb2bd3fe0e6194e816c2d1da7c361725f5eb89a910/msgspec-0.21.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:846758412e9518252b2ac9bffd6f0e54d9ff614f5f9488df7749f81ff5c80920", size = 218871, upload-time = "2026-04-12T21:44:09.917Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d1/d4cd9fe89c7d400d7a18f86ccc94daa3f0927f53558846fcb60791dce5d6/msgspec-0.21.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21995e74b5c598c2e004110ad66ec7f1b8c20bf2bcf3b2de8fd9a3094422d3ff", size = 225025, upload-time = "2026-04-12T21:44:11.191Z" }, + { url = "https://files.pythonhosted.org/packages/24/bf/e20549e602b9edccadeeff98760345a416f9cce846a657e8b18e3396b212/msgspec-0.21.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6129f0cca52992e898fd5344187f7c8127b63d810b2fd73e36fca73b4c6475ee", size = 222672, upload-time = "2026-04-12T21:44:12.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/68/04d7a8f0f786545cf9b8c280c57aa6befb5977af6e884b8b54191cbe44b3/msgspec-0.21.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ef3ec2296248d1f8b9231acb051b6d471dfde8f21819e86c9adaaa9f42918521", size = 227303, upload-time = "2026-04-12T21:44:13.709Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4d/619866af2840875be408047bf9e70ceafbae6ab50660de7134ed1b25eb86/msgspec-0.21.1-cp312-cp312-win_amd64.whl", hash = "sha256:d4ab834a054c6f0cbeef6df9e7e1b33d5f1bc7b86dea1d2fd7cad003873e783d", size = 190017, upload-time = "2026-04-12T21:44:14.977Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2e/a8f9eca8fd00e097d7a9e99ba8a4685db994494448e3d4f0b7f6e9a3c0f7/msgspec-0.21.1-cp312-cp312-win_arm64.whl", hash = "sha256:628aaa35c74950a8c59da330d7e98917e1c7188f983745782027748ee4ca573e", size = 175345, upload-time = "2026-04-12T21:44:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/7e/74/f11ede02839b19ff459f88e3145df5d711626ca84da4e23520cebf819367/msgspec-0.21.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:764173717a01743f007e9f74520ed281f24672c604514f7d76c1c3a10e8edb66", size = 196176, upload-time = "2026-04-12T21:44:17.613Z" }, + { url = "https://files.pythonhosted.org/packages/bb/40/4476c1bd341418a046c4955aff632ec769315d1e3cb94e6acf86d461f9ed/msgspec-0.21.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:344c7cd0eaed1fb81d7959f99100ef71ec9b536881a376f11b9a6c4803365697", size = 188524, upload-time = "2026-04-12T21:44:18.815Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d9/9e9d7d7e5061b47540d03d640fab9b3965ba7ae49c1b2154861c8f007518/msgspec-0.21.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48943e278b3854c2f89f955ddc6f9f430d3f0784b16e47d10604ee0463cd21f5", size = 218880, upload-time = "2026-04-12T21:44:20.028Z" }, + { url = "https://files.pythonhosted.org/packages/74/66/2bb344f34abb4b57e60c7c9c761994e0417b9718ec1460bf00c296f2a7ea/msgspec-0.21.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9aa659ebb0101b1cbc31461212b87e341d961f0ab0772aaf068a99e001ec4aa", size = 225050, upload-time = "2026-04-12T21:44:21.577Z" }, + { url = "https://files.pythonhosted.org/packages/1a/84/7c1e412f76092277bf760cef12b7979d03314d259ab5b5cafde5d0c1722d/msgspec-0.21.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7b27d1a8ead2b6f5b0c4f2d07b8be1ccfcc041c8a0e704781edebe3ae13c484", size = 222713, upload-time = "2026-04-12T21:44:22.83Z" }, + { url = "https://files.pythonhosted.org/packages/4e/27/0bba04b2b4ef05f3d068429410bc71d2cea925f1596a8f41152cccd5edb8/msgspec-0.21.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38fe93e86b61328fe544cb7fd871fad5a27c8734bfda90f65e5dbe288ae50f61", size = 227259, upload-time = "2026-04-12T21:44:24.11Z" }, + { url = "https://files.pythonhosted.org/packages/b0/2d/09574b0eea02fed2c2c1383dbaae2c7f79dc16dcd6487a886000afb5d7c4/msgspec-0.21.1-cp313-cp313-win_amd64.whl", hash = "sha256:8bc666331c35fcce05a7cd2d6221adbe0f6058f8e750711413d22793c080ac6a", size = 189857, upload-time = "2026-04-12T21:44:25.359Z" }, + { url = "https://files.pythonhosted.org/packages/46/34/105b1576ad182879914f0c821f17ee1d13abb165cb060448f96fe2aff078/msgspec-0.21.1-cp313-cp313-win_arm64.whl", hash = "sha256:42bb1241e0750c1a4346f2aa84db26c5ffd99a4eb3a954927d9f149ff2f42898", size = 175403, upload-time = "2026-04-12T21:44:26.608Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ad/86954e987d1d6a5c579e2c2e7832b65e0fff194179fdac4f581536086024/msgspec-0.21.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fab48eb45fdbfbdb2c0edfec00ffc53b6b6085beefc6b50b61e01659f9f8757f", size = 196261, upload-time = "2026-04-12T21:44:27.807Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a1/c5e46c3e42b866199365e35d11dddfd1fbd8bba4fdb3c52f965b1607ce94/msgspec-0.21.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3cb779ea0c35bc807ff941d415875c1f69ca0be91a2e907ab99a171811d86a9a", size = 188729, upload-time = "2026-04-12T21:44:28.99Z" }, + { url = "https://files.pythonhosted.org/packages/85/7d/1e29a319d678d6cb962ae5bdf32a6858ebdf38f73bc654c0e9c742a0c2c8/msgspec-0.21.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68604db36b3b4dd9bf160e436e12798a4738848144cea1aca1cb984011eb160f", size = 219866, upload-time = "2026-04-12T21:44:31.104Z" }, + { url = "https://files.pythonhosted.org/packages/25/1f/cca084ca2572810fff12ea9dbdcbe39eac048f40daf4a9077b49fcbe8cee/msgspec-0.21.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d6b9dc50948eaf65df54d2fd0ff66e6d8c32f116037209ee861810eb9b676cb", size = 224993, upload-time = "2026-04-12T21:44:32.649Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/d2120fc9d419a89a3a7c13e5b7078798c4b392a96a02a6e2b3ce43a8766c/msgspec-0.21.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:52c5e21930942302394429c5a582ce7e6b62c7f983b3760834c2ce107e0dd6df", size = 223535, upload-time = "2026-04-12T21:44:33.839Z" }, + { url = "https://files.pythonhosted.org/packages/75/17/42418b66a3ad972a89bab73dd78b79cc6282bb488a25e73c853cee7443b9/msgspec-0.21.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:abbb39d65681fa24ed394e01af3d59d869068324f900c61d06062b7fb9980f2f", size = 227222, upload-time = "2026-04-12T21:44:35.093Z" }, + { url = "https://files.pythonhosted.org/packages/c4/33/265c894268cca88ff67b144ca2b4c522fc8b9a6f1966a3640c70516e78e1/msgspec-0.21.1-cp314-cp314-win_amd64.whl", hash = "sha256:5666b1b560b97b6ec2eb3fca8a502298ebac56e13bbca1f88523538ce83d01ea", size = 193810, upload-time = "2026-04-12T21:44:36.612Z" }, + { url = "https://files.pythonhosted.org/packages/3b/8f/a6d35f25bf1fc63c492fdd88fdce01ba0875ead48c2b91f90f33653b4131/msgspec-0.21.1-cp314-cp314-win_arm64.whl", hash = "sha256:d8b8578e4c83b14ceea4cef0d0b747e31d9330fe4b03b2b2ad4063866a178f93", size = 179125, upload-time = "2026-04-12T21:44:38.198Z" }, + { url = "https://files.pythonhosted.org/packages/c6/39/74839641e64b99d87da55af0fc472854d42b46e2183b9e2a67fe1bb2a512/msgspec-0.21.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15f523d51c00ebad412213bfe9f06f0a50ec2b93e0c19e824a2d267cabb48ea2", size = 200171, upload-time = "2026-04-12T21:44:39.414Z" }, + { url = "https://files.pythonhosted.org/packages/70/9b/ce0cca6d2d87fcd4b6ff97600790494e64f26a2c55d61507cd2755c16193/msgspec-0.21.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e47390360583ba3d5c6cb44cf0a9f61b0a06a899d3c2c00627cedebb2e2884b", size = 192879, upload-time = "2026-04-12T21:44:40.882Z" }, + { url = "https://files.pythonhosted.org/packages/a7/08/673a7bb05e5702dc787ddd3011195b509f9867927970da59052211929987/msgspec-0.21.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f60800e6299b798142dc40b0644da77ceac5ea0568be58228417eae14135c847", size = 226281, upload-time = "2026-04-12T21:44:42.181Z" }, + { url = "https://files.pythonhosted.org/packages/7d/45/86508cf57283e9070b3c447e3ab25b792a7a0855a3ea4e0c6d111ac34c97/msgspec-0.21.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5f8e9dfcd98419cf7568808470c4317a3fb30bef0e3715b568730a2b272a20d7", size = 229863, upload-time = "2026-04-12T21:44:43.442Z" }, + { url = "https://files.pythonhosted.org/packages/2c/62/e7c9367cd08d590559faacd711edbae36840342843e669440363f33c7d36/msgspec-0.21.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92d89dfad13bd1ea640dc3e37e724ed380da1030b272bdf5ecafb983c3ad7c75", size = 230445, upload-time = "2026-04-12T21:44:44.806Z" }, + { url = "https://files.pythonhosted.org/packages/42/b4/c0f54632103846b658a10930025f4de41c8724b5e4805a5f3b395586cb7e/msgspec-0.21.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0d03867786e5d7ba25d666df4b11320c27170f4aeafcb8e3a8b0a50a4fb742ca", size = 231822, upload-time = "2026-04-12T21:44:46.343Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1d/0d85cc79d0ccf5508e9c846cc66552a6a16bf92abd1dbd8362617f7b35cd/msgspec-0.21.1-cp314-cp314t-win_amd64.whl", hash = "sha256:740fbf1c9d59992ca3537d6fbe9ebbf9eaf726a65fbf31448e0ecbc710697a63", size = 206650, upload-time = "2026-04-12T21:44:47.601Z" }, + { url = "https://files.pythonhosted.org/packages/90/91/56c5d560f20e6c20e9e4f55bd0e458f7f162aa689ee350346c04c48eac0b/msgspec-0.21.1-cp314-cp314t-win_arm64.whl", hash = "sha256:0d2cc73df6058d811a126ac3a8ad63a4dfa210c82f9cf5a004802eaf4712de90", size = 183149, upload-time = "2026-04-12T21:44:48.833Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "multiprocess" +version = "0.70.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" }, + { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414, upload-time = "2026-01-19T06:47:35.915Z" }, + { url = "https://files.pythonhosted.org/packages/a0/61/af9115673a5870fd885247e2f1b68c4f1197737da315b520a91c757a861a/multiprocess-0.70.19-py314-none-any.whl", hash = "sha256:e8cc7fbdff15c0613f0a1f1f8744bef961b0a164c0ca29bdff53e9d2d93c5e5f", size = 160318, upload-time = "2026-01-19T06:47:37.497Z" }, + { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "ninja" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/73/79a0b22fc731989c708068427579e840a6cf4e937fe7ae5c5d0b7356ac22/ninja-1.13.0.tar.gz", hash = "sha256:4a40ce995ded54d9dc24f8ea37ff3bf62ad192b547f6c7126e7e25045e76f978", size = 242558, upload-time = "2025-08-11T15:10:19.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/74/d02409ed2aa865e051b7edda22ad416a39d81a84980f544f8de717cab133/ninja-1.13.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:fa2a8bfc62e31b08f83127d1613d10821775a0eb334197154c4d6067b7068ff1", size = 310125, upload-time = "2025-08-11T15:09:50.971Z" }, + { url = "https://files.pythonhosted.org/packages/8e/de/6e1cd6b84b412ac1ef327b76f0641aeb5dcc01e9d3f9eee0286d0c34fd93/ninja-1.13.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3d00c692fb717fd511abeb44b8c5d00340c36938c12d6538ba989fe764e79630", size = 177467, upload-time = "2025-08-11T15:09:52.767Z" }, + { url = "https://files.pythonhosted.org/packages/c8/83/49320fb6e58ae3c079381e333575fdbcf1cca3506ee160a2dcce775046fa/ninja-1.13.0-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:be7f478ff9f96a128b599a964fc60a6a87b9fa332ee1bd44fa243ac88d50291c", size = 187834, upload-time = "2025-08-11T15:09:54.115Z" }, + { url = "https://files.pythonhosted.org/packages/56/c7/ba22748fb59f7f896b609cd3e568d28a0a367a6d953c24c461fe04fc4433/ninja-1.13.0-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:60056592cf495e9a6a4bea3cd178903056ecb0943e4de45a2ea825edb6dc8d3e", size = 202736, upload-time = "2025-08-11T15:09:55.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/22/d1de07632b78ac8e6b785f41fa9aad7a978ec8c0a1bf15772def36d77aac/ninja-1.13.0-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1c97223cdda0417f414bf864cfb73b72d8777e57ebb279c5f6de368de0062988", size = 179034, upload-time = "2025-08-11T15:09:57.394Z" }, + { url = "https://files.pythonhosted.org/packages/ed/de/0e6edf44d6a04dabd0318a519125ed0415ce437ad5a1ec9b9be03d9048cf/ninja-1.13.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fb46acf6b93b8dd0322adc3a4945452a4e774b75b91293bafcc7b7f8e6517dfa", size = 180716, upload-time = "2025-08-11T15:09:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/54/28/938b562f9057aaa4d6bfbeaa05e81899a47aebb3ba6751e36c027a7f5ff7/ninja-1.13.0-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4be9c1b082d244b1ad7ef41eb8ab088aae8c109a9f3f0b3e56a252d3e00f42c1", size = 146843, upload-time = "2025-08-11T15:10:00.046Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fb/d06a3838de4f8ab866e44ee52a797b5491df823901c54943b2adb0389fbb/ninja-1.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:6739d3352073341ad284246f81339a384eec091d9851a886dfa5b00a6d48b3e2", size = 154402, upload-time = "2025-08-11T15:10:01.657Z" }, + { url = "https://files.pythonhosted.org/packages/31/bf/0d7808af695ceddc763cf251b84a9892cd7f51622dc8b4c89d5012779f06/ninja-1.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11be2d22027bde06f14c343f01d31446747dbb51e72d00decca2eb99be911e2f", size = 552388, upload-time = "2025-08-11T15:10:03.349Z" }, + { url = "https://files.pythonhosted.org/packages/9d/70/c99d0c2c809f992752453cce312848abb3b1607e56d4cd1b6cded317351a/ninja-1.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:aa45b4037b313c2f698bc13306239b8b93b4680eb47e287773156ac9e9304714", size = 472501, upload-time = "2025-08-11T15:10:04.735Z" }, + { url = "https://files.pythonhosted.org/packages/9f/43/c217b1153f0e499652f5e0766da8523ce3480f0a951039c7af115e224d55/ninja-1.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f8e1e8a1a30835eeb51db05cf5a67151ad37542f5a4af2a438e9490915e5b72", size = 638280, upload-time = "2025-08-11T15:10:06.512Z" }, + { url = "https://files.pythonhosted.org/packages/8c/45/9151bba2c8d0ae2b6260f71696330590de5850e5574b7b5694dce6023e20/ninja-1.13.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:3d7d7779d12cb20c6d054c61b702139fd23a7a964ec8f2c823f1ab1b084150db", size = 642420, upload-time = "2025-08-11T15:10:08.35Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/95752eb635bb8ad27d101d71bef15bc63049de23f299e312878fc21cb2da/ninja-1.13.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:d741a5e6754e0bda767e3274a0f0deeef4807f1fec6c0d7921a0244018926ae5", size = 585106, upload-time = "2025-08-11T15:10:09.818Z" }, + { url = "https://files.pythonhosted.org/packages/c1/31/aa56a1a286703800c0cbe39fb4e82811c277772dc8cd084f442dd8e2938a/ninja-1.13.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:e8bad11f8a00b64137e9b315b137d8bb6cbf3086fbdc43bf1f90fd33324d2e96", size = 707138, upload-time = "2025-08-11T15:10:11.366Z" }, + { url = "https://files.pythonhosted.org/packages/34/6f/5f5a54a1041af945130abdb2b8529cbef0cdcbbf9bcf3f4195378319d29a/ninja-1.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b4f2a072db3c0f944c32793e91532d8948d20d9ab83da9c0c7c15b5768072200", size = 581758, upload-time = "2025-08-11T15:10:13.295Z" }, + { url = "https://files.pythonhosted.org/packages/95/97/51359c77527d45943fe7a94d00a3843b81162e6c4244b3579fe8fc54cb9c/ninja-1.13.0-py3-none-win32.whl", hash = "sha256:8cfbb80b4a53456ae8a39f90ae3d7a2129f45ea164f43fadfa15dc38c4aef1c9", size = 267201, upload-time = "2025-08-11T15:10:15.158Z" }, + { url = "https://files.pythonhosted.org/packages/29/45/c0adfbfb0b5895aa18cec400c535b4f7ff3e52536e0403602fc1a23f7de9/ninja-1.13.0-py3-none-win_amd64.whl", hash = "sha256:fb8ee8719f8af47fed145cced4a85f0755dd55d45b2bddaf7431fa89803c5f3e", size = 309975, upload-time = "2025-08-11T15:10:16.697Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/a7b983643d1253bb223234b5b226e69de6cda02b76cdca7770f684b795f5/ninja-1.13.0-py3-none-win_arm64.whl", hash = "sha256:3c0b40b1f0bba764644385319028650087b4c1b18cdfa6f45cb39a3669b81aa9", size = 290806, upload-time = "2025-08-11T15:10:18.018Z" }, +] + +[[package]] +name = "numba" +version = "0.65.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llvmlite" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/61/7299643b9c18d669e04be7c5bcb64d985070d07553274817b45b049e7bfe/numba-0.65.0.tar.gz", hash = "sha256:edad0d9f6682e93624c00125a471ae4df186175d71fd604c983c377cdc03e68b", size = 2764131, upload-time = "2026-04-01T03:52:01.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/2f/8bd31a1ea43c01ac215283d83aa5f8d5acbe7a36c85b82f1757bfe9ccb31/numba-0.65.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b27ee4847e1bfb17e9604d100417ee7c1d10f15a6711c6213404b3da13a0b2aa", size = 2680705, upload-time = "2026-04-01T03:51:32.597Z" }, + { url = "https://files.pythonhosted.org/packages/73/36/88406bd58600cc696417b8e5dd6a056478da808f3eaf48d18e2421e0c2d9/numba-0.65.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a52d92ffd297c10364bce60cd1fcb88f99284ab5df085f2c6bcd1cb33b529a6f", size = 3801411, upload-time = "2026-04-01T03:51:34.321Z" }, + { url = "https://files.pythonhosted.org/packages/0c/61/ce753a1d7646dd477e16d15e89473703faebb8995d2f71d7ad69a540b565/numba-0.65.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da8e371e328c06d0010c3d8b44b21858652831b85bcfba78cb22c042e22dbd8e", size = 3501622, upload-time = "2026-04-01T03:51:36.348Z" }, + { url = "https://files.pythonhosted.org/packages/7d/86/db87a5393f1b1fabef53ac3ba4e6b938bb27e40a04ad7cc512098fcae032/numba-0.65.0-cp312-cp312-win_amd64.whl", hash = "sha256:59bb9f2bb9f1238dfd8e927ba50645c18ae769fef4f3d58ea0ea22a2683b91f5", size = 2749979, upload-time = "2026-04-01T03:51:37.88Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/eee0f1ff456218db036bfc9023995ec1f85a9dc8f2422f1594f6a87829e0/numba-0.65.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c6334094563a456a695c812e6846288376ca02327cf246cdcc83e1bb27862367", size = 2680679, upload-time = "2026-04-01T03:51:39.491Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8f/3d116e4b8e92f6abace431afa4b2b944f4d65bdee83af886f5c4b263df95/numba-0.65.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b8a9008411615c69d083d1dcf477f75a5aa727b30beb16e139799e2be945cdfd", size = 3809537, upload-time = "2026-04-01T03:51:41.42Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2c/6a3ca4128e253cb67affe06deb47688f51ce968f5111e2a06d010e6f1fa6/numba-0.65.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af96c0cba53664efcb361528b8c75e011a6556c859c7e08424c2715201c6cf7a", size = 3508615, upload-time = "2026-04-01T03:51:43.444Z" }, + { url = "https://files.pythonhosted.org/packages/96/0e/267f9a36fb282c104a971d7eecb685b411c47dce2a740fe69cf5fc2945d9/numba-0.65.0-cp313-cp313-win_amd64.whl", hash = "sha256:6254e73b9c929dc736a1fbd3d6f5680789709a5067cae1fa7198707385129c04", size = 2749938, upload-time = "2026-04-01T03:51:45.218Z" }, + { url = "https://files.pythonhosted.org/packages/56/a4/90edb01e9176053578e343d7a7276bc28356741ee67059aed8ed2c1a4e59/numba-0.65.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:ee336b398a6fca51b1f626034de99f50cb1bd87d537a166275158a3cee744b82", size = 2680878, upload-time = "2026-04-01T03:51:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/24/8d/e12d6ff4b9119db3cbf7b2db1ce257576441bd3c76388c786dea74f20b02/numba-0.65.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:05c0a9fdf75d85f57dee47b719e8d6415707b80aae45d75f63f9dc1b935c29f7", size = 3778456, upload-time = "2026-04-01T03:51:48.552Z" }, + { url = "https://files.pythonhosted.org/packages/17/89/abcd83e76f6a773276fe76244140671bcc5bf820f6e2ae1a15362ae4c8c9/numba-0.65.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:583680e0e8faf124d362df23b4b593f3221a8996341a63d1b664c122401bec2f", size = 3478464, upload-time = "2026-04-01T03:51:50.527Z" }, + { url = "https://files.pythonhosted.org/packages/73/5b/fbce55ce3d933afbc7ade04df826853e4a846aaa47d58d2fbb669b8f2d08/numba-0.65.0-cp314-cp314-win_amd64.whl", hash = "sha256:add297d3e1c08dd884f44100152612fa41e66a51d15fdf91307f9dde31d06830", size = 2752012, upload-time = "2026-04-01T03:51:52.691Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ab/af705f4257d9388fb2fd6d7416573e98b6ca9c786e8b58f02720978557bd/numba-0.65.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:194a243ba53a9157c8538cbb3166ec015d785a8c5d584d06cdd88bee902233c7", size = 2683961, upload-time = "2026-04-01T03:51:54.281Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e5/8267b0adb0c01b52b553df5062fbbb42c30ed5362d08b85cc913a36f838f/numba-0.65.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7fa502960f7a2f3f5cb025bc7bff888a3551277b92431bfdc5ba2f11a375749", size = 3816373, upload-time = "2026-04-01T03:51:56.18Z" }, + { url = "https://files.pythonhosted.org/packages/b0/f5/b8397ca360971669a93706b9274592b6864e4367a37d498fbbcb62aa2d48/numba-0.65.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5046c63f783ca3eb6195f826a50797465e7c4ce811daa17c9bea47e310c9b964", size = 3532782, upload-time = "2026-04-01T03:51:58.387Z" }, + { url = "https://files.pythonhosted.org/packages/f5/21/1e73fa16bf0393ebb74c5bb208d712152ffdfc84600a8e93a3180317856e/numba-0.65.0-cp314-cp314t-win_amd64.whl", hash = "sha256:46fd679ae4f68c7a5d5721efbd29ecee0b0f3013211591891d79b51bfdf73113", size = 2757611, upload-time = "2026-04-01T03:52:00.083Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" }, + { url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" }, + { url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" }, + { url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" }, + { url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" }, + { url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" }, + { url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" }, + { url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" }, + { url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" }, + { url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" }, + { url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" }, + { url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" }, + { url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" }, + { url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" }, + { url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" }, + { url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" }, + { url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" }, + { url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" }, + { url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" }, + { url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" }, + { url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" }, + { url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" }, + { url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" }, + { url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" }, + { url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" }, + { url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" }, + { url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" }, + { url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" }, + { url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" }, + { url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" }, + { url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" }, + { url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" }, + { url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" }, + { url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" }, + { url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" }, + { url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, +] + +[[package]] +name = "nvidia-cublas" +version = "13.1.0.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226, upload-time = "2025-10-09T08:59:04.818Z" }, + { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236, upload-time = "2025-10-09T08:59:32.536Z" }, +] + +[[package]] +name = "nvidia-cuda-cccl" +version = "13.3.3.3.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/7a/9cb8a7fb87a85b11e8753548ae1422be847c5dddf3ca9ff5b080b309e271/nvidia_cuda_cccl-13.3.3.3.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4dbc9dd84fbaeae267cbd80a9ed76d35171dba78639695dbdff0bae50e4503fa", size = 3453010, upload-time = "2026-05-26T16:27:45.179Z" }, + { url = "https://files.pythonhosted.org/packages/fe/fb/195d50d25ab68a76b817ffc68c45b1fb828598ce35a8e5c1736060628dab/nvidia_cuda_cccl-13.3.3.3.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40ba1fa0b2c694ddc06cc791ed5c8bdad4638e2735b784960d68ac3086399c97", size = 3453013, upload-time = "2026-05-26T16:28:08.209Z" }, + { url = "https://files.pythonhosted.org/packages/57/44/37cf1596880e7712f357b3f4991cd34d0f322c26e2bc814d1bdeffb2f420/nvidia_cuda_cccl-13.3.3.3.1-py3-none-win_amd64.whl", hash = "sha256:d1ac746f57ab83403f01e64e2b292101caf5b3445babca9f1c1c34f344766adf", size = 3452993, upload-time = "2026-05-26T16:58:59.166Z" }, +] + +[[package]] +name = "nvidia-cuda-crt" +version = "13.3.33" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/32/5ea57f8cd6ad5df2173d175ac5db4e06edde40028b1b1f6c539ea4c10290/nvidia_cuda_crt-13.3.33-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8c257393f9c9146a85d3644f352be8154843d760031f756e673222c768a4930", size = 157348, upload-time = "2026-05-26T16:28:40.446Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a7/998af901511d5efdc6e42fc597d32a69f34eecf86f1591a9d230ab3ab951/nvidia_cuda_crt-13.3.33-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ff37600c7b880a14cab4ade763b4c10c0ff92f25cc9dca30f0881ce52693c4", size = 157350, upload-time = "2026-05-26T16:29:22.315Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/fc8ce6b7719c825e0e519d2922e3b7630238e860222ad3f972dd9b8b7fa9/nvidia_cuda_crt-13.3.33-py3-none-win_amd64.whl", hash = "sha256:7e89c6dbb807a47ee0628907488b158e57c36fa31af3756a8f826a9ec482715f", size = 158284, upload-time = "2026-05-26T16:59:37.309Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, +] + +[[package]] +name = "nvidia-cuda-nvcc" +version = "13.3.33" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cuda-crt" }, + { name = "nvidia-cuda-runtime", version = "13.0.96", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'darwin'" }, + { name = "nvidia-cuda-runtime", version = "13.3.29", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" }, + { name = "nvidia-nvvm" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/b6/bb07a3a63b5b7b55516366747892abbf3ee62d616684c40bb51e6cbfe956/nvidia_cuda_nvcc-13.3.33-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c348623b1434aebd234da9ec1f81022587ae4995d65c3dc8a7743245cc441f7", size = 39515074, upload-time = "2026-05-26T16:34:28.489Z" }, + { url = "https://files.pythonhosted.org/packages/3f/af/e1b107f034f7c133255c162b922bbad3da5be20ebf76df17662ae4bd31f6/nvidia_cuda_nvcc-13.3.33-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53b5f1be1731574368b8be931b77b6313492266c464aef3dd3f431569ce90deb", size = 44943276, upload-time = "2026-05-26T16:35:05.912Z" }, + { url = "https://files.pythonhosted.org/packages/47/c2/831fa54020621a64d44cff47f1ed5eb0611794495fce01c857f6999d76b1/nvidia_cuda_nvcc-13.3.33-py3-none-win_amd64.whl", hash = "sha256:21c93aeef695a81b688137119f9120fe08a67292bf0ad730d94dc2b18bec23f0", size = 32723421, upload-time = "2026-05-26T17:01:47.511Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, + { url = "https://files.pythonhosted.org/packages/4a/af/345fedb9f4c76c84ab4fa445b36bd4048a4d9db60e6bc76b4f913ff4b852/nvidia_cuda_nvrtc-13.0.88-py3-none-win_amd64.whl", hash = "sha256:6bcd4e7f8e205cbe644f5a98f2f799bef9556fefc89dd786e79a16312ce49872", size = 76807835, upload-time = "2025-09-04T08:39:15.274Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.3.33" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version < '3.13' and sys_platform == 'darwin'", +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/b7/94/6b867483bec07da24ffa32736c79fabb94ef3a7af4d787a9d4a974868576/nvidia_cuda_runtime-13.0.96-py3-none-win_amd64.whl", hash = "sha256:f79298c8a098cec150a597c8eba58ecdab96e3bdc4b9bc4f9983635031740492", size = 2927037, upload-time = "2025-10-09T09:04:23.782Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.3.29" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version < '3.13' and sys_platform == 'darwin'", +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.19.0.56" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, + { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321, upload-time = "2026-02-03T20:44:52.837Z" }, +] + +[[package]] +name = "nvidia-cudnn-frontend" +version = "1.18.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/b4/604e230378680ee117849a4e1045baca092f93161a829291a84d5acce70c/nvidia_cudnn_frontend-1.18.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:310b417f2848a83d1437203fcaeea320a74fb7f28af20bf42bf5afc9c01f1c12", size = 2027408, upload-time = "2026-01-27T23:32:46.576Z" }, + { url = "https://files.pythonhosted.org/packages/c6/52/08f98262e77b1cbcc834cc1a5db494d0661ea1dbdea58c2e2d51a57fdaca/nvidia_cudnn_frontend-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c023539ca6de99234cf5102c3ec0d6af817f5396fc93028a22ba5b834a35b8a", size = 2159245, upload-time = "2026-01-27T23:07:32.664Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1f/751a5a8cfdc95fb4dc556192d37369ae488c30c473fe9a3ec720b23d07ea/nvidia_cudnn_frontend-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:e13f7dd46cdb4762dde87f181f06d1c5e15e9478bbdd547bfa74d9b11f415aae", size = 1591041, upload-time = "2026-01-27T23:09:04.118Z" }, + { url = "https://files.pythonhosted.org/packages/e8/bd/db791a26ebb6a6e1268f518e18c82d8ad18546f7008f4b0d5bde15f927de/nvidia_cudnn_frontend-1.18.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a6e2b7bd43705ffa4af3b187374fdd5e7d09fc228a4d65fc8b4b0a537a8e605", size = 2027249, upload-time = "2026-01-27T23:33:22.46Z" }, + { url = "https://files.pythonhosted.org/packages/19/74/3038cf496d5de7cfdff730f5202e438c17d9123de507059340e02ddff9d7/nvidia_cudnn_frontend-1.18.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0544206b02cae9da4f044ca3fe7416b99e0c8a8052285dd3e5a8fc445d34f9c", size = 2160001, upload-time = "2026-01-27T23:07:50.248Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5e/148cc6609dba326e620e4d949246020dfba05ca07d0387442e62b71d19b6/nvidia_cudnn_frontend-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:7eefa5f10cc003df5f3593f82f1ee6c001fc3412bdc78430c751914dfceefd7f", size = 1591270, upload-time = "2026-01-27T23:09:21.435Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0a/515209dd2afc6027bf1112bf415f575bfe9628d18877abe7424cb597dd7b/nvidia_cudnn_frontend-1.18.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b489da1b30f1d7da822b37b89cc4f68afd80e020eb57e4ab24921f8b57f6e946", size = 2028689, upload-time = "2026-02-11T21:32:04.235Z" }, + { url = "https://files.pythonhosted.org/packages/ab/57/52d18e1f50979eeabfafb408ec73068afc5a1e1ccd21636240317cd456d4/nvidia_cudnn_frontend-1.18.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37688c81a34ac590aff9de4c34d2968bab949411af707baa327616ebd4b34ae1", size = 2160182, upload-time = "2026-02-11T21:25:18.437Z" }, + { url = "https://files.pythonhosted.org/packages/67/53/df2810b56d259ef96fa6beaa1381bd14c29fbe82836b409516e864c5e177/nvidia_cudnn_frontend-1.18.0-cp314-cp314-win_amd64.whl", hash = "sha256:5053b473fa74168b5fbf35934cd6187f88aa03b8447b9f2cd417332d5e5c9569", size = 1592759, upload-time = "2026-02-11T21:32:33.87Z" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cusparse", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277, upload-time = "2025-08-13T19:22:40.982Z" }, + { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119, upload-time = "2025-08-13T19:23:41.967Z" }, +] + +[[package]] +name = "nvidia-cutlass-dsl" +version = "4.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cutlass-dsl-libs-base" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/15/575d7df4fe2f3406f1cfc68be72aeff2834f8a696daf1cd5bee8017e4507/nvidia_cutlass_dsl-4.5.2-py3-none-any.whl", hash = "sha256:68ed1b63ca74aae87955012da9dfd7fdaae471329d0028b229b841c7192ccf52", size = 10179, upload-time = "2026-05-25T03:38:56.364Z" }, +] + +[package.optional-dependencies] +cu13 = [ + { name = "nvidia-cutlass-dsl-libs-cu13" }, +] + +[[package]] +name = "nvidia-cutlass-dsl-libs-base" +version = "4.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-python" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/ef/e827e3c67d72adbf4e8f680bdf03b1b67723d9e1ae7c3d0a1751f39f69ce/nvidia_cutlass_dsl_libs_base-4.5.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d2a3c412287e356fbe48fe9f845d6d33cd35dea5e20d7e4f628c20957967cacd", size = 75643473, upload-time = "2026-05-25T03:49:15.857Z" }, + { url = "https://files.pythonhosted.org/packages/97/68/c1247ab848f26c4ab56e562eea0e3f31fc14c9aaf0d883afaa92d8f05592/nvidia_cutlass_dsl_libs_base-4.5.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:15ef6a59193667e663934ef4873f8ccad37455e9b7c3c419c3072113b8aedf61", size = 74513226, upload-time = "2026-05-25T03:51:32.496Z" }, + { url = "https://files.pythonhosted.org/packages/b0/f8/b192015e273ff023a35741d6d5e4a93e4819160dee3955fc5d3d53534450/nvidia_cutlass_dsl_libs_base-4.5.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:395bd77cf642aeef311313453e6582f11c9357a4b81fe620ea3daccd1fccab9b", size = 75645002, upload-time = "2026-05-25T03:48:01.887Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/bfe256ac08e5a6dfb11444809e54c76c3a2f05fff38dd173e2e71b95e4d2/nvidia_cutlass_dsl_libs_base-4.5.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e59da7d89e5e4f8514c6530843f910f9d8734d8042dcaa079c9d9c5063eb3514", size = 74514312, upload-time = "2026-05-25T03:50:56.343Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/7a5de500bb74915ab8b3875f4952ae07d562f33d06eef9b2569adf4c09ab/nvidia_cutlass_dsl_libs_base-4.5.2-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:216eee6aa8107d35569f9451b66b03a3c53167841d1af9b630b966ef8d966e19", size = 75636795, upload-time = "2026-05-25T03:47:31.081Z" }, + { url = "https://files.pythonhosted.org/packages/3e/bc/5f9dd8c05c3e2f435228224f0b0e76e324c1bf0a6dcd3cfb917b5e94bad7/nvidia_cutlass_dsl_libs_base-4.5.2-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:12c29f7c1f1f82851092ba3869264dafafb035228c0d9827a8db08b884fb80ca", size = 74511193, upload-time = "2026-05-25T03:52:39.444Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/76a9d1ce5ade3f43ab6f10e361a9c1962d02177deeaf46f2c3684a7ae959/nvidia_cutlass_dsl_libs_base-4.5.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:5aca392063ffbc7da30442a267928b22d4a2d37f9ea1db32e4487aa31b0fcc33", size = 75644393, upload-time = "2026-05-25T03:47:02.706Z" }, + { url = "https://files.pythonhosted.org/packages/15/84/08d695d2e0fa95891a2e5abd978f359d50125e4d1f056e54697d465fccc3/nvidia_cutlass_dsl_libs_base-4.5.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:abab8a0d2f3f5661533c366df78f973052b86a3b52b868d997a95dce5aa8f17b", size = 74514399, upload-time = "2026-05-25T03:50:20.841Z" }, +] + +[[package]] +name = "nvidia-cutlass-dsl-libs-cu13" +version = "4.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-python" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "nvidia-cutlass-dsl-libs-base" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/e5/aeb570713a7bd6c2cb08102c2ebe6de234ef1bbc276d1af4643266cd71a8/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3032405dff28892340f96b467e744a822079cae454dce534fc17b77e85190e42", size = 79084280, upload-time = "2026-05-25T03:40:57.547Z" }, + { url = "https://files.pythonhosted.org/packages/03/60/443e559139da15ab544761ac14f4206dffb981af48cc9856cd5b5b7cf0e7/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:80f0cd402e0f1d1571e5aed33bfa17dbc9cb90cc5b1352f0f806b4788558e80e", size = 78759198, upload-time = "2026-05-25T03:45:59.297Z" }, + { url = "https://files.pythonhosted.org/packages/98/57/bc7248c02c3e4ee2ed03e194ceda9861a46fa23f0da5140bd8060a086b1e/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:64e994554af4da59f75754b9df1a2b1bdfdb96b58c2457802da13d586fb58cde", size = 79086223, upload-time = "2026-05-25T03:41:27.363Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9f/b7928ff505e577c1021c07b206ce32d285aae793763d524023c1800b6dc9/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c7a5ce1c01616fc4c3ac492e011c543a79c3dde86aaf20a8af55e9d40ef2b2e6", size = 78759546, upload-time = "2026-05-25T03:45:25.834Z" }, + { url = "https://files.pythonhosted.org/packages/d1/65/a8e16a9647acef4f43ea2e046cd7eeb3e5779e89089c3939a5d25fe47f57/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:aabd41c980083db94950a4010c2c1ca156d4ab56701605739a3fba388ac9736b", size = 79087704, upload-time = "2026-05-25T03:41:57.202Z" }, + { url = "https://files.pythonhosted.org/packages/aa/83/d335575e1d37f6c436b1e3203ded6f352678937b9f30b900b643f9df0f9d/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:888edad4fe1e9b683fddcbc6969437527ccd0eb8740e60dce8f29f6a3a22c825", size = 78758331, upload-time = "2026-05-25T03:43:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/89/7c/2bf50f2649f06a97a935919f71d2d0e40d7648364319b834548ed664d6d3/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c4d3ea9080c5a92f8f4a69451ef7036f43bfc3d7f8a426dd70258f0e237c05fb", size = 79092929, upload-time = "2026-05-25T03:39:51.239Z" }, + { url = "https://files.pythonhosted.org/packages/36/80/8ced4c7e1ead8d1e3ac6c823db9e387dbcfd41232e11655d5bc94e950c75/nvidia_cutlass_dsl_libs_cu13-4.5.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1d255f4a308eb0d228d2466a415a8489b8337db1d322f5d8428e60139b41a317", size = 78765148, upload-time = "2026-05-25T03:44:52.411Z" }, +] + +[[package]] +name = "nvidia-ml-py" +version = "13.595.45" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/49/c29f6e30d8662d2e94fef17739ea7309cc76aba269922ae999e4cc07f268/nvidia_ml_py-13.595.45.tar.gz", hash = "sha256:c9f34897fe0441ff35bc8f35baf80f830a20b0f4e6ce71e0a325bc0e66acf079", size = 50780, upload-time = "2026-03-19T16:59:44.956Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/24/fc256107d23597fa33d319505ce77160fa1a2349c096d01901ffc7cb7fc4/nvidia_ml_py-13.595.45-py3-none-any.whl", hash = "sha256:b65a7977f503d56154b14d683710125ef93594adb63fbf7e559336e3318f1376", size = 51776, upload-time = "2026-03-19T16:59:43.603Z" }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.28.9" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677, upload-time = "2025-11-18T05:49:03.45Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177, upload-time = "2025-11-18T05:49:17.677Z" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, +] + +[[package]] +name = "nvidia-nvvm" +version = "13.3.33" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/8a/f767031dcd0d24c2bbab4b696dbcf004da4f3284e5e4649fc47bc0e2bb78/nvidia_nvvm-13.3.33-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:aafaf73246b6126bc88f521e5dab1d196395ee87739d9f5b7c39c9fee0ead9c7", size = 69250604, upload-time = "2026-05-26T16:57:56.875Z" }, + { url = "https://files.pythonhosted.org/packages/83/36/ce0d42d3a4465c858c379932f0080d29d22f04383ab79119c7c4f4cdd5ef/nvidia_nvvm-13.3.33-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd74a1c5ef284ba04c1ba75f886404dff953c54731a3a9c7b45e9aedaf1a226b", size = 66984524, upload-time = "2026-05-26T16:57:30.778Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/4de7a37803d168337ab36f81ecbc496c7c21c9b06ec68ce0ecc381af88d4/nvidia_nvvm-13.3.33-py3-none-win_amd64.whl", hash = "sha256:b1c63cf8972d8a1ff153c5ac4cc7038fe6ef705aa38415f12007b0e5e4c31b79", size = 60175824, upload-time = "2026-05-26T17:13:27.588Z" }, +] + +[[package]] +name = "openai" +version = "2.38.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/12/cfa322c5f5dd8fa21aab9a7a8e979e7a11123800f86ca8d82eb68a83d213/openai-2.38.0.tar.gz", hash = "sha256:798694c6cf74145541fda94325b6f8f72d8e1fd0262cc137c8d728177a6a4ce3", size = 772764, upload-time = "2026-05-21T21:23:42.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/bf/ccff9be562e24207716d04ef9dc931c76aff0c89a7265da43e2104d7fe06/openai-2.38.0-py3-none-any.whl", hash = "sha256:ec6661c57b2dcc47414a767e6e3335c7ed3d19c9696999283a3c82e95c756a3c", size = 1344910, upload-time = "2026-05-21T21:23:39.636Z" }, +] + +[[package]] +name = "openai-harmony" +version = "0.0.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/92/2d038d096f29179c7c9571b431f9e739f87a487121901725e23fe338dd9d/openai_harmony-0.0.8.tar.gz", hash = "sha256:6e43f98e6c242fa2de6f8ea12eab24af63fa2ed3e89c06341fb9d92632c5cbdf", size = 284777, upload-time = "2025-11-05T19:07:06.727Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/c6/2502f416d46be3ec08bb66d696cccffb57781a499e3ff2e4d7c174af4e8f/openai_harmony-0.0.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:029ec25ca74abe48fdb58eb9fdd2a8c1618581fc33ce8e5653f8a1ffbfbd9326", size = 2627806, upload-time = "2025-11-05T19:06:57.063Z" }, + { url = "https://files.pythonhosted.org/packages/d3/d2/ce6953ca87db9cae3e775024184da7d1c5cb88cead19a2d75b42f00a959c/openai_harmony-0.0.8-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4f709815924ec325b9a890e6ab2bbb0ceec8e319a4e257328eb752cf36b2efc", size = 2948463, upload-time = "2025-11-05T19:06:48.17Z" }, + { url = "https://files.pythonhosted.org/packages/fa/4c/b553c9651662d6ce102ca7f3629d268b23df1abe5841e24bed81e8a8e949/openai_harmony-0.0.8-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5cfcfd963b50a41fc656c84d3440ca6eecdccd6c552158ce790b8f2e33dfb5a9", size = 2704083, upload-time = "2025-11-05T19:06:50.205Z" }, + { url = "https://files.pythonhosted.org/packages/9b/af/4eec8f9ab9c27bcdb444460c72cf43011d176fc44c79d6e113094ca1e152/openai_harmony-0.0.8-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a3a16972aa1cee38ea958470cd04ac9a2d5ac38fdcf77ab686611246220c158", size = 2959765, upload-time = "2025-11-05T19:06:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/11/3c/33f3374e4624e0e776f6b13b73c45a7ead7f9c4529f8369ed5bfcaa30cac/openai_harmony-0.0.8-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4d5cfa168e74d08f8ba6d58a7e49bc7daef4d58951ec69b66b0d56f4927a68d", size = 3427031, upload-time = "2025-11-05T19:06:51.829Z" }, + { url = "https://files.pythonhosted.org/packages/25/3f/1a192b93bb47c6b44cd98ba8cc1d3d2a9308f1bb700c3017e6352da11bda/openai_harmony-0.0.8-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c007d277218a50db8839e599ed78e0fffe5130f614c3f6d93ae257f282071a29", size = 2953260, upload-time = "2025-11-05T19:06:55.406Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/93b582cad3531797c3db7c2db5400fd841538ccddfd9f5e3df61be99a630/openai_harmony-0.0.8-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8565d4f5a0638da1bffde29832ed63c9e695c558611053add3b2dc0b56c92dbc", size = 3127044, upload-time = "2025-11-05T19:06:59.553Z" }, + { url = "https://files.pythonhosted.org/packages/1d/10/4327dbf87f75ae813405fd9a9b4a5cde63d506ffed0a096a440a4cabd89c/openai_harmony-0.0.8-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:cbaa3bda75ef0d8836e1f8cc84af62f971b1d756d740efc95c38c3e04c0bfde2", size = 2932931, upload-time = "2025-11-05T19:07:01.437Z" }, + { url = "https://files.pythonhosted.org/packages/8a/c8/1774eec4f6f360ef57618fb8f52e3d3af245b2491bd0297513aa09eec04b/openai_harmony-0.0.8-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:772922a9bd24e133950fad71eb1550836f415a88e8c77870e12d0c3bd688ddc2", size = 2996140, upload-time = "2025-11-05T19:07:03.438Z" }, + { url = "https://files.pythonhosted.org/packages/60/c3/3d1e01e2dba517a91760e4a03e4f20ffc75039a6fe584d0e6f9b5c78fd15/openai_harmony-0.0.8-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:007b0476a1f331f8130783f901f1da6f5a7057af1a4891f1b6a31dec364189b5", size = 3205080, upload-time = "2025-11-05T19:07:05.078Z" }, + { url = "https://files.pythonhosted.org/packages/14/63/119de431572d7c70a7bf1037034a9be6ed0a7502a7498ba7302bca5b3242/openai_harmony-0.0.8-cp38-abi3-win32.whl", hash = "sha256:a9b5f893326b28d9e935ade14b4f655f5a840942473bc89b201c25f7a15af9cf", size = 2082457, upload-time = "2025-11-05T19:07:09.631Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/c83cf5a206c263ee70448a5ae4264682555f4d0b5bed0d2cc6ca1108103d/openai_harmony-0.0.8-cp38-abi3-win_amd64.whl", hash = "sha256:39d44f0d8f466bd56698e7ead708bead3141e27b9b87e3ab7d5a6d0e4a869ee5", size = 2438369, upload-time = "2025-11-05T19:07:08.1Z" }, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, +] + +[[package]] +name = "opencv-python-headless" +version = "4.13.0.92" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/42/2310883be3b8826ac58c3f2787b9358a2d46923d61f88fedf930bc59c60c/opencv_python_headless-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:1a7d040ac656c11b8c38677cc8cccdc149f98535089dbe5b081e80a4e5903209", size = 46247192, upload-time = "2026-02-05T07:01:35.187Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1e/6f9e38005a6f7f22af785df42a43139d0e20f169eb5787ce8be37ee7fcc9/opencv_python_headless-4.13.0.92-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:3e0a6f0a37994ec6ce5f59e936be21d5d6384a4556f2d2da9c2f9c5dc948394c", size = 32568914, upload-time = "2026-02-05T07:01:51.989Z" }, + { url = "https://files.pythonhosted.org/packages/21/76/9417a6aef9def70e467a5bf560579f816148a4c658b7d525581b356eda9e/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c8cfc8e87ed452b5cecb9419473ee5560a989859fe1d10d1ce11ae87b09a2cb", size = 33703709, upload-time = "2026-02-05T10:24:46.469Z" }, + { url = "https://files.pythonhosted.org/packages/92/ce/bd17ff5772938267fd49716e94ca24f616ff4cb1ff4c6be13085108037be/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0525a3d2c0b46c611e2130b5fdebc94cf404845d8fa64d2f3a3b679572a5bd22", size = 56016764, upload-time = "2026-02-05T10:26:48.904Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b4/b7bcbf7c874665825a8c8e1097e93ea25d1f1d210a3e20d4451d01da30aa/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb60e36b237b1ebd40a912da5384b348df8ed534f6f644d8e0b4f103e272ba7d", size = 35010236, upload-time = "2026-02-05T10:28:11.031Z" }, + { url = "https://files.pythonhosted.org/packages/4b/33/b5db29a6c00eb8f50708110d8d453747ca125c8b805bc437b289dbdcc057/opencv_python_headless-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0bd48544f77c68b2941392fcdf9bcd2b9cdf00e98cb8c29b2455d194763cf99e", size = 60391106, upload-time = "2026-02-05T10:30:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c3/52cfea47cd33e53e8c0fbd6e7c800b457245c1fda7d61660b4ffe9596a7f/opencv_python_headless-4.13.0.92-cp37-abi3-win32.whl", hash = "sha256:a7cf08e5b191f4ebb530791acc0825a7986e0d0dee2a3c491184bd8599848a4b", size = 30812232, upload-time = "2026-02-05T07:02:29.594Z" }, + { url = "https://files.pythonhosted.org/packages/4a/90/b338326131ccb2aaa3c2c85d00f41822c0050139a4bfe723cfd95455bd2d/opencv_python_headless-4.13.0.92-cp37-abi3-win_amd64.whl", hash = "sha256:77a82fe35ddcec0f62c15f2ba8a12ecc2ed4207c17b0902c7a3151ae29f37fb6", size = 40070414, upload-time = "2026-02-05T07:02:26.448Z" }, +] + +[[package]] +name = "openenv-core" +version = "0.3.0" +source = { editable = "../../" } +dependencies = [ + { name = "aiohttp" }, + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "gradio" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, + { name = "tomli" }, + { name = "tomli-w" }, + { name = "typer" }, + { name = "uvicorn" }, + { name = "websockets" }, +] + +[package.optional-dependencies] +core = [ + { name = "fastapi" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "uvicorn" }, + { name = "websockets" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.13.5" }, + { name = "daytona", marker = "extra == 'daytona'", specifier = ">=0.136.0" }, + { name = "docutils", marker = "extra == 'docs'", specifier = ">=0.18.1,<0.23" }, + { name = "fastapi", specifier = ">=0.104.0" }, + { name = "fastapi", marker = "extra == 'core'", specifier = ">=0.104.0" }, + { name = "fastmcp", specifier = ">=3.0.0" }, + { name = "gradio", specifier = ">=4.0.0" }, + { name = "httpx", specifier = ">=0.28.1" }, + { name = "huggingface-hub", specifier = ">=0.20.0" }, + { name = "huggingface-hub", marker = "extra == 'cli'", specifier = ">=0.20.0" }, + { name = "inspect-ai", marker = "extra == 'inspect'", specifier = ">=0.3.0" }, + { name = "matplotlib", marker = "extra == 'docs'" }, + { name = "myst-parser", marker = "extra == 'docs'" }, + { name = "nest-asyncio", marker = "extra == 'docs'" }, + { name = "openai", specifier = ">=2.7.2" }, + { name = "openai", marker = "extra == 'cli'", specifier = ">=2.7.2" }, + { name = "openenv-core", extras = ["cli"], marker = "extra == 'all'" }, + { name = "openenv-core", extras = ["core"], marker = "extra == 'all'" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "pydantic", marker = "extra == 'core'", specifier = ">=2.0.0" }, + { name = "pytorch-sphinx-theme2", marker = "extra == 'docs'" }, + { name = "pyyaml", specifier = ">=6.0" }, + { name = "pyyaml", marker = "extra == 'cli'", specifier = ">=6.0" }, + { name = "pyyaml", marker = "extra == 'daytona'", specifier = ">=6.0" }, + { name = "requests", specifier = ">=2.25.0" }, + { name = "requests", marker = "extra == 'core'", specifier = ">=2.25.0" }, + { name = "rich", specifier = ">=13.0.0" }, + { name = "rich", marker = "extra == 'cli'", specifier = ">=13.0.0" }, + { name = "smolagents", marker = "extra == 'docs'" }, + { name = "sphinx", marker = "extra == 'docs'", specifier = "==7.2.6" }, + { name = "sphinx-design", marker = "extra == 'docs'", specifier = "==0.6.1" }, + { name = "sphinx-gallery", marker = "extra == 'docs'", specifier = ">=0.14.0" }, + { name = "sphinx-sitemap", marker = "extra == 'docs'", specifier = "==2.9.0" }, + { name = "sphinxcontrib-katex", marker = "extra == 'docs'", specifier = "==0.9.11" }, + { name = "sphinxcontrib-mermaid", marker = "extra == 'docs'", specifier = "==2.0.1" }, + { name = "sphinxext-opengraph", marker = "extra == 'docs'" }, + { name = "tomli", specifier = ">=2.3.0" }, + { name = "tomli", marker = "extra == 'cli'", specifier = ">=2.3.0" }, + { name = "tomli-w", specifier = ">=1.2.0" }, + { name = "tomli-w", marker = "extra == 'cli'", specifier = ">=1.2.0" }, + { name = "typer", specifier = ">=0.9.0" }, + { name = "typer", marker = "extra == 'cli'", specifier = ">=0.9.0" }, + { name = "uvicorn", specifier = ">=0.24.0" }, + { name = "uvicorn", marker = "extra == 'core'", specifier = ">=0.24.0" }, + { name = "websockets", specifier = ">=15.0.1" }, + { name = "websockets", marker = "extra == 'core'", specifier = ">=15.0.1" }, +] +provides-extras = ["core", "cli", "docs", "all", "daytona", "inspect"] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=7.0" }, + { name = "pytest-asyncio", specifier = ">=0.21" }, + { name = "ruff", specifier = ">=0.14.0" }, + { name = "usort", specifier = ">=1.1.0" }, +] + +[[package]] +name = "openenv-mini-swe-env" +version = "0.1.0" +source = { editable = "../../envs/mini_swe_env" } +dependencies = [ + { name = "aiohttp" }, + { name = "datasets" }, + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "openenv-core", extra = ["core"] }, + { name = "pydantic" }, + { name = "requests" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.13.5" }, + { name = "datasets", specifier = ">=4.8.0" }, + { name = "fastapi", specifier = ">=0.104.0" }, + { name = "fastmcp", specifier = ">=3.3.0" }, + { name = "openenv-core", extras = ["core"], specifier = ">=0.3.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, + { name = "requests", specifier = ">=2.25.0" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0" }, +] +provides-extras = ["dev"] + +[[package]] +name = "opentelemetry-api" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/1c/125e1c936c0873796771b7f04f6c93b9f1bf5d424cea90fda94a99f61da8/opentelemetry_api-1.42.1.tar.gz", hash = "sha256:56c63bea9f77b62856be8c47600474acad853b2924b99b1687c4cb6297166716", size = 72296, upload-time = "2026-05-21T16:32:49.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/ca/9520cc1f3dfbbd03ac5903bbf55833e257bc64b1cf30fa8b0d6df374d821/opentelemetry_api-1.42.1-py3-none-any.whl", hash = "sha256:51a69edacadbc03a8950ace1c4c21099cacc538820ac2c9e36277e78cebba714", size = 61311, upload-time = "2026-05-21T16:32:28.822Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/94/8637919a5d01f81dacf510234bc0110b944f4687a6e96b0a02adf2f6bdce/opentelemetry_exporter_otlp-1.42.1.tar.gz", hash = "sha256:2d9ebaed714377a67d224d46795ddcc11d2c877fa5de35fda70b6f3b010729a9", size = 6086, upload-time = "2026-05-21T16:32:51.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/4d/c26080295a36fd22e201fefd7cb9c22cd203189b1af8cd73b158382b7ad8/opentelemetry_exporter_otlp-1.42.1-py3-none-any.whl", hash = "sha256:aedd54545bb0587cd45210abdc8be545af9c01413f3307786e276df1e3c83bee", size = 6733, upload-time = "2026-05-21T16:32:31.261Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/9c/216acfeaedadf2e1937f4373929b20f73197c5c4a2546d4f584b7fa63813/opentelemetry_exporter_otlp_proto_common-1.42.1.tar.gz", hash = "sha256:04f1f01fb597c4249dfcd7f8b861c902c2102369d376d9d346ff38de4469a2ee", size = 21433, upload-time = "2026-05-21T16:32:55.526Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/43/2375e7612e1121a4518c17603b6e0b03ad94f565aafad53f464dc5be2bf6/opentelemetry_exporter_otlp_proto_common-1.42.1-py3-none-any.whl", hash = "sha256:f48d395ab815b444da118868977e9798ea354c25737d5cf39578ae894011c140", size = 17327, upload-time = "2026-05-21T16:32:33.387Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/87/ca7fc790dfdbcf4f9e9aab14a39ef1b7508ead13707e283de0b3131478d2/opentelemetry_exporter_otlp_proto_grpc-1.42.1.tar.gz", hash = "sha256:975c4461f167dd8ed8857d68d3b6b25f3d272eab896f6a9470d0f5b90e2faf15", size = 27140, upload-time = "2026-05-21T16:32:56.162Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/2b/28ba5b128f47fe8c3bab541000d6feb4b5a9bd26623ca013406f01c0fb60/opentelemetry_exporter_otlp_proto_grpc-1.42.1-py3-none-any.whl", hash = "sha256:0ae1177e2038b18a929b3098215243631ef91136cba26b7e2b12790ceb7e87cc", size = 19617, upload-time = "2026-05-21T16:32:34.278Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/32/826bfa1d80ecea24f47808de03cd4a0d13c17ecc07712f45123f0f61e4ac/opentelemetry_exporter_otlp_proto_http-1.42.1.tar.gz", hash = "sha256:bf142a21035d7571ac3a09cb2e5639f49886f243972883cfe777ed3bf02b734d", size = 25406, upload-time = "2026-05-21T16:32:56.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/96/82cb223a1502f0787d4bbff12907f5f8d870a50731febcd5818d93ef9555/opentelemetry_exporter_otlp_proto_http-1.42.1-py3-none-any.whl", hash = "sha256:00a16da1b312a1d6c7233d600d557c91df71125af73020f3b9a7765bd699d59d", size = 21793, upload-time = "2026-05-21T16:32:35.277Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/55/63eac3e1089b768ba014091fdd2ae8a9a440c821ef5e2b786909c94c8836/opentelemetry_proto-1.42.1.tar.gz", hash = "sha256:c6a51e6b4f05ae63565f3a113217f3d2bfaec68f78c02d7a6c85f9010d1cfca6", size = 45839, upload-time = "2026-05-21T16:33:03.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/9d/171c02c84a76940b7e601805b3bb536985aded9168fbcc9ba52f0a730fa2/opentelemetry_proto-1.42.1-py3-none-any.whl", hash = "sha256:dedb74cba2886c59c7789b227a7a670613025a07489040050aedff6e5c0fb43c", size = 71782, upload-time = "2026-05-21T16:32:44.867Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.42.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f7/b390bd9bfd703bf98a68fea1f27786c6872331fd617164a54b8a59bdc008/opentelemetry_sdk-1.42.1.tar.gz", hash = "sha256:8c834e8f8c9ba4171d4ec843d0cb8a67e4c7394d3f9e9297e582cbd9456ddbf7", size = 239262, upload-time = "2026-05-21T16:33:04.641Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/6b/4287766cfbde577ae2272e8884abac325aeaac0d64f41c61d5b8cc595105/opentelemetry_sdk-1.42.1-py3-none-any.whl", hash = "sha256:083cd4bbfaa5aa7b5a9e552430d9951219967cfb27aa61feb13a77aba1fc839d", size = 170907, upload-time = "2026-05-21T16:32:45.894Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.63b1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/99/4d7dd6df64795951413ce6e815f8cf1eb191daf7196ae86574589643d5f3/opentelemetry_semantic_conventions-0.63b1.tar.gz", hash = "sha256:3daf963611334b365e98a57438183eb012d3bfb40b2d931a9af613476b8701a9", size = 148340, upload-time = "2026-05-21T16:33:05.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/7a/7fe66f5f3682b1dd47d88cc4e11f1c6c0966b737de2d16671146e23c39a5/opentelemetry_semantic_conventions-0.63b1-py3-none-any.whl", hash = "sha256:dfe5ef4dee82586b746f522b818ceb298d00b3d59f660042bd79404bff8d0682", size = 203713, upload-time = "2026-05-21T16:32:47.016Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions-ai" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-sdk" }, + { name = "opentelemetry-semantic-conventions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/02/10aeacc37a38a3a8fa16ff67bec1ae3bf882539f6f9efb0f70acf802ca2d/opentelemetry_semantic_conventions_ai-0.5.1.tar.gz", hash = "sha256:153906200d8c1d2f8e09bd78dbef526916023de85ac3dab35912bfafb69ff04c", size = 26533, upload-time = "2026-03-26T14:20:38.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/22/41fb05f1dc5fda2c468e05a41814c20859016c85117b66c8a257cae814f6/opentelemetry_semantic_conventions_ai-0.5.1-py3-none-any.whl", hash = "sha256:25aeb22bd261543b4898a73824026d96770e5351209c7d07a0b1314762b1f6e4", size = 11250, upload-time = "2026-03-26T14:20:37.108Z" }, +] + +[[package]] +name = "orjson" +version = "3.11.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" }, + { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" }, + { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" }, + { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" }, + { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" }, + { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" }, + { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" }, + { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" }, + { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" }, + { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" }, + { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" }, + { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" }, + { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" }, + { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" }, + { url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" }, + { url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" }, + { url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" }, +] + +[[package]] +name = "outlines-core" +version = "0.2.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/04/4a0812eb27c086cfd2e66e7ec9150f33e105912a9b7f8b335e3479f03a06/outlines_core-0.2.14.tar.gz", hash = "sha256:64808deed1591ca3029ff64346ceb974cd5d780c916ea82504951fe83523039e", size = 191539, upload-time = "2026-01-09T15:59:10.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/93/30b9188648a479b32be429a24166db47a7bfdb0f9a8aac4c6dcf569e0a52/outlines_core-0.2.14-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:95e6476d9702d2fcc4e85370dbbfb6933a46c816e9c90107f6ce36eb68b5d64a", size = 2049651, upload-time = "2026-01-09T15:58:28.549Z" }, + { url = "https://files.pythonhosted.org/packages/0d/06/f3557daa8e87d5b95f64de269a301d73ec3c2202ab897c3e1f1cb93eb1db/outlines_core-0.2.14-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:f04731a5e29a190e2cc9f692a1f3fb2414a645355ca7d01b83df43439c38bea8", size = 2201046, upload-time = "2026-01-09T15:58:29.958Z" }, + { url = "https://files.pythonhosted.org/packages/0c/67/d8acf778990964c951080d568284e858d466f27dfd6f2674781927faba1c/outlines_core-0.2.14-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:0e4c69f0a8565edb56464c4c9b6c291a10805f3a96dff84182980e90ae1a5e2f", size = 2049558, upload-time = "2026-01-09T15:58:31.003Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/0320b14b49b8379ced1ab195ecf5875dbd2267b90148847541f43bfde6c1/outlines_core-0.2.14-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:63f53cfd9614e754499ae86dd699f3abcecf42d6a4e58d80fd80347881d85960", size = 2197854, upload-time = "2026-01-09T15:58:32.39Z" }, + { url = "https://files.pythonhosted.org/packages/29/29/3a04944407207a5d214879ca5ca33c2bd3e65199a4e927051c1bdaaa4d50/outlines_core-0.2.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bb2060c240c4507f334965a8948dbeeb22007560d797f6debd92346c0b620cb", size = 2341426, upload-time = "2026-01-09T15:58:33.553Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a7/a77f746272504bac3f628047d56ea1731b61549a3e1d9bbfd226f2968246/outlines_core-0.2.14-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1de34681c7e0e7e1551fc9036e4fa3c57986336c905a10536591ceb6d869c258", size = 2236941, upload-time = "2026-01-09T15:58:35.118Z" }, + { url = "https://files.pythonhosted.org/packages/99/0d/9f599d938923ab8ceeff26fdf2f9ea53bea3c962085c4927a08338a32349/outlines_core-0.2.14-cp312-cp312-win32.whl", hash = "sha256:870e8e038853818cb202ccc8cde92251f300f96805bfcc3be1c883adda7b5297", size = 1842940, upload-time = "2026-01-09T15:58:36.544Z" }, + { url = "https://files.pythonhosted.org/packages/f8/df/0f145c52ebd156d80273e2f5278227ea57e0275b2aa863bed33f44f77923/outlines_core-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:87b42440478764cce1353a87d8560ef82f3b39b9d753bfe93195ea3584f369e3", size = 2137266, upload-time = "2026-01-09T15:58:37.831Z" }, + { url = "https://files.pythonhosted.org/packages/13/9d/e6c81c975c123f0639d5f6909c987e510d43e07c2e1e6495b21639c4dec6/outlines_core-0.2.14-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8b3e8d668188282a1f7666732bb8a01958ab134db35bb792e7442a40e55ff1e7", size = 2049297, upload-time = "2026-01-09T15:58:39.184Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d1/5ce55ef724aed0915edc877b6dd610d39b3169e4341154bb53daa022065a/outlines_core-0.2.14-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:66e695b375b180725fb534d9adf298531c152ec3d881e3b9e01c82b5dd269f52", size = 2200944, upload-time = "2026-01-09T15:58:40.257Z" }, + { url = "https://files.pythonhosted.org/packages/32/e3/60ad781251eedcf1496317ecd58eb2e4488717ba63b10494ab49dfd05e5d/outlines_core-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:6bd166d3b07acef2f60d4ede44592a26d3f7d8712876bfc8e22150045def5857", size = 2049607, upload-time = "2026-01-09T15:58:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/bc/2d/662d6a76face5b4b3481f888900d00856c37aa2927341a023866457da212/outlines_core-0.2.14-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:9d45462d7548aa0e17176a691ae73447f3e6bed9658a0cd96fe72eadf7474475", size = 2197755, upload-time = "2026-01-09T15:58:42.861Z" }, + { url = "https://files.pythonhosted.org/packages/c1/9a/4b62903de006d991b58674ff033c1b6fb92be5767360376fc961f6771bdb/outlines_core-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6453e23f01d98ec48e3a4141d7112792ce77001dfb28d91d6fd89f47009f91ef", size = 2341051, upload-time = "2026-01-09T15:58:44.415Z" }, + { url = "https://files.pythonhosted.org/packages/50/36/1532f7d9ab16c676812d94528e89964aa0d15f12adcb285e6ed86f86f2fe/outlines_core-0.2.14-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7deef6df74cb247f2a3a62f03438ba967456504b0555ec7029f8db834e054448", size = 2236778, upload-time = "2026-01-09T15:58:45.437Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5a/dfd94f15f4c04e691e7fdf30cf8b9b22bf2cbc426b3ef270af3e200596d5/outlines_core-0.2.14-cp313-cp313-win32.whl", hash = "sha256:bb008c7ecc034bcfda0ddc10a4d1f2181a4b61ec1643ee56183dd6fa64139c9d", size = 1842727, upload-time = "2026-01-09T15:58:46.723Z" }, + { url = "https://files.pythonhosted.org/packages/34/35/e24ab5d2116812464380587435297d8ece2f0218c2ba8afc9f541e3a6911/outlines_core-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:eb27e92204b296a063ac58f361153be4e78c8103a96e0b1c085b22d4fc3534cf", size = 2137108, upload-time = "2026-01-09T15:58:47.784Z" }, + { url = "https://files.pythonhosted.org/packages/1b/28/22fe8ee3bdf9cf13ab88a9d9b96729d9966c791c25227d0b7ca45c8d118f/outlines_core-0.2.14-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:69410e5b55bcbaad8c865d94bd01e7bff8a57996dcd2251b7d50dec70d7d9a63", size = 2050470, upload-time = "2026-01-09T15:58:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/d4/3e/30ce0b13e4c4c82de606c8bbf60775ac6fca1978efa54cd553893795fd0b/outlines_core-0.2.14-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:adf96395759d7fdf6efeb8a67d3f36f520c1546bfd4df0752306db8c7cb7d6c5", size = 2202138, upload-time = "2026-01-09T15:58:50.281Z" }, + { url = "https://files.pythonhosted.org/packages/53/13/bd2ff9e90b28fa0dcc345c9196731ed9126e366733c8ccbc559149e34893/outlines_core-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:b02bb0fc21c5e23e2ff9b2d1459db2c1c3e813a7646c9d5db091c6931edb9c85", size = 2050325, upload-time = "2026-01-09T15:58:51.596Z" }, + { url = "https://files.pythonhosted.org/packages/1e/25/fc0ae7d04345d17267d4dd5c693ed9e86c7f44419cc04ad92348472781be/outlines_core-0.2.14-cp314-cp314-macosx_15_0_x86_64.whl", hash = "sha256:e75395b1cccecdf85d8d8265aba28841ddeb1e8da406f4b1e0135df5a6e9960f", size = 2199081, upload-time = "2026-01-09T15:58:53.17Z" }, + { url = "https://files.pythonhosted.org/packages/d5/63/dfa000239e46f17b47e6dc9bec3aab8a8136fe400312f1916320e02c8f38/outlines_core-0.2.14-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1776ae984574461f249fe590314a439992eb9b883f4091b8fa7fc56f29f3717", size = 2343210, upload-time = "2026-01-09T15:58:54.282Z" }, + { url = "https://files.pythonhosted.org/packages/36/4f/0e63da06c6054f154ef22b5ef3c6b9030cb22da9c03d2d2dd82836a1e795/outlines_core-0.2.14-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:7eba2b41dac03d6e6e8d5ea0aecbbc03dacb4c57de3b1fc944d0bafb022941f7", size = 2238206, upload-time = "2026-01-09T15:58:55.705Z" }, + { url = "https://files.pythonhosted.org/packages/74/4e/382271ab5ffe768055f11dddb50e82a0c46487f3766bf08a06cfcd35388b/outlines_core-0.2.14-cp314-cp314-win32.whl", hash = "sha256:0cd8ce3ce61df44fd9c5450d9744e2280586c2a6e6e3dfefa0dab1944764b424", size = 1845364, upload-time = "2026-01-09T15:58:56.795Z" }, + { url = "https://files.pythonhosted.org/packages/0d/11/13adf2d02c681b599c1eb550b0dbd763d1b818a106a13bd693019bdb5637/outlines_core-0.2.14-cp314-cp314-win_amd64.whl", hash = "sha256:3e67fc23b1a3ac9562488fb50f409c171538b76f64aa5f7e25d9b0bf14770204", size = 2139979, upload-time = "2026-01-09T15:58:57.984Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, +] + +[[package]] +name = "partial-json-parser" +version = "0.2.1.1.post7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/6d/eed37d7ebc1e0bcd27b831c0cf1fe94881934316187c4b30d23f29ea0bd4/partial_json_parser-0.2.1.1.post7.tar.gz", hash = "sha256:86590e1ba6bcb6739a2dfc17d2323f028cb5884f4c6ce23db376999132c9a922", size = 10296, upload-time = "2025-11-17T07:27:41.202Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/32/658973117bf0fd82a24abbfb94fe73a5e86216e49342985e10acce54775a/partial_json_parser-0.2.1.1.post7-py3-none-any.whl", hash = "sha256:145119e5eabcf80cbb13844a6b50a85c68bf99d376f8ed771e2a3c3b03e653ae", size = 10877, upload-time = "2025-11-17T07:27:40.457Z" }, +] + +[[package]] +name = "pathable" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/f3/5a20387de9bcd0607871bfc2198ee0e15836da7baa4592ccd7f24c27c986/pathable-0.6.0.tar.gz", hash = "sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58", size = 18970, upload-time = "2026-05-19T18:15:11.911Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/e8/6d75ffd9784bce2e93d1ae4415649427e39a53bb172d4672b2b59c6f0a7b/pathable-0.6.0-py3-none-any.whl", hash = "sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566", size = 18983, upload-time = "2026-05-19T18:15:10.728Z" }, +] + +[[package]] +name = "peft" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accelerate" }, + { name = "huggingface-hub" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/cf/037f1e3d5186496c05513a6754639e2dab3038a05f384284d49a9bd06a2d/peft-0.19.1.tar.gz", hash = "sha256:0d97542fe96dcdaa20d3b81c06f26f988618f416a73544ab23c3618ccb674a40", size = 763738, upload-time = "2026-04-16T15:46:45.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/b6/f54d676ed93cc2dd2234c3b172ea9c8c3d7d29361e66b1b23dec57a67465/peft-0.19.1-py3-none-any.whl", hash = "sha256:2113f72a81621b5913ef28f9022204c742df111890c5f49d812716a4a301e356", size = 680692, upload-time = "2026-04-16T15:46:42.886Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "prometheus-client" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035, upload-time = "2026-04-09T19:53:42.359Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154, upload-time = "2026-04-09T19:53:41.324Z" }, +] + +[[package]] +name = "prometheus-fastapi-instrumentator" +version = "8.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "prometheus-client" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/7a/fe49b7060e585e2cfa28cc1d13ebfe9c2904dcd80cf11071d5de0f622ff2/prometheus_fastapi_instrumentator-8.0.0.tar.gz", hash = "sha256:c31ed192db077e75467b28b2fe5055362517f53369b798cb8dac9ff85f3f1c38", size = 20357, upload-time = "2026-05-29T13:04:43.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/ad/b14ed2fe73bb1d37bcc947e75afae018f795526415d5b849aeb99c403cd1/prometheus_fastapi_instrumentator-8.0.0-py3-none-any.whl", hash = "sha256:e3c967c402124dfe81cf5aad35208d9f96db5452c6cb752350d9358ca0a96198", size = 19411, upload-time = "2026-05-29T13:04:44.17Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716, upload-time = "2022-10-25T20:38:06.303Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335, upload-time = "2022-10-25T20:38:27.636Z" }, +] + +[[package]] +name = "py-key-value-aio" +version = "0.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/e2/d689d922894a7ecde73b6daeaf9b13dab5aae06fe6aaaf7514722644d382/py_key_value_aio-0.4.5.tar.gz", hash = "sha256:c6563a2c6abe5da5e20f4f9e875c2a9b425a2244a54fadbf46cf140a9eea45d7", size = 107547, upload-time = "2026-05-27T16:37:08.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/95/b8ba862968712caa12a19666175334fa979e1f198b896a430adb3bacfe87/py_key_value_aio-0.4.5-py3-none-any.whl", hash = "sha256:ab862adbcb8c72547d1c57821f22cbbb71ab86509039c96f36e914e0336c8dd7", size = 170005, upload-time = "2026-05-27T16:37:06.629Z" }, +] + +[package.optional-dependencies] +filetree = [ + { name = "aiofile" }, + { name = "anyio" }, +] +keyring = [ + { name = "keyring" }, +] +memory = [ + { name = "cachetools" }, +] + +[[package]] +name = "pyarrow" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997, upload-time = "2026-04-21T10:49:48.796Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720, upload-time = "2026-04-21T10:49:55.858Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852, upload-time = "2026-04-21T10:50:04.624Z" }, + { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852, upload-time = "2026-04-21T10:50:12.293Z" }, + { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207, upload-time = "2026-04-21T10:50:20.677Z" }, + { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117, upload-time = "2026-04-21T10:50:29.14Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155, upload-time = "2026-04-21T10:51:22.337Z" }, + { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387, upload-time = "2026-04-21T10:50:35.552Z" }, + { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102, upload-time = "2026-04-21T10:50:42.417Z" }, + { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118, upload-time = "2026-04-21T10:50:49.324Z" }, + { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765, upload-time = "2026-04-21T10:50:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890, upload-time = "2026-04-21T10:51:02.439Z" }, + { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250, upload-time = "2026-04-21T10:51:10.576Z" }, + { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282, upload-time = "2026-04-21T10:51:16.815Z" }, +] + +[[package]] +name = "pybase64" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, + { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, + { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, + { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, + { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, + { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, + { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, + { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, + { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, + { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, + { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, + { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, + { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, + { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, + { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, + { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, + { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, + { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, + { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, + { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, + { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, + { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, + { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, + { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, + { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, + { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, + { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, + { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, + { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, + { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, + { url = "https://files.pythonhosted.org/packages/e4/80/4bd3dff423e5a91f667ca41982dc0b79495b90ec0c0f5d59aca513e50f8c/pybase64-1.4.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:015bb586a1ea1467f69d57427abe587469392215f59db14f1f5c39b52fdafaf5", size = 33835, upload-time = "2025-12-06T13:24:31.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/60/a94d94cc1e3057f602e0b483c9ebdaef40911d84a232647a2fe593ab77bb/pybase64-1.4.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d101e3a516f837c3dcc0e5a0b7db09582ebf99ed670865223123fb2e5839c6c0", size = 40673, upload-time = "2025-12-06T13:24:32.82Z" }, + { url = "https://files.pythonhosted.org/packages/e3/71/cf62b261d431857e8e054537a5c3c24caafa331de30daede7b2c6c558501/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8f183ac925a48046abe047360fe3a1b28327afb35309892132fe1915d62fb282", size = 30939, upload-time = "2025-12-06T13:24:34.001Z" }, + { url = "https://files.pythonhosted.org/packages/24/3e/d12f92a3c1f7c6ab5d53c155bff9f1084ba997a37a39a4f781ccba9455f3/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30bf3558e24dcce4da5248dcf6d73792adfcf4f504246967e9db155be4c439ad", size = 31401, upload-time = "2025-12-06T13:24:35.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3d/9c27440031fea0d05146f8b70a460feb95d8b4e3d9ca8f45c972efb4c3d3/pybase64-1.4.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a674b419de318d2ce54387dd62646731efa32b4b590907800f0bd40675c1771d", size = 38075, upload-time = "2025-12-06T13:24:36.53Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d4/6c0e0cf0efd53c254173fbcd84a3d8fcbf5e0f66622473da425becec32a5/pybase64-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:720104fd7303d07bac302be0ff8f7f9f126f2f45c1edb4f48fdb0ff267e69fe1", size = 38257, upload-time = "2025-12-06T13:24:38.049Z" }, + { url = "https://files.pythonhosted.org/packages/50/eb/27cb0b610d5cd70f5ad0d66c14ad21c04b8db930f7139818e8fbdc14df4d/pybase64-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:83f1067f73fa5afbc3efc0565cecc6ed53260eccddef2ebe43a8ce2b99ea0e0a", size = 31685, upload-time = "2025-12-06T13:24:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/db/26/b136a4b65e5c94ff06217f7726478df3f31ab1c777c2c02cf698e748183f/pybase64-1.4.3-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b51204d349a4b208287a8aa5b5422be3baa88abf6cc8ff97ccbda34919bbc857", size = 68460, upload-time = "2025-12-06T13:24:41.735Z" }, + { url = "https://files.pythonhosted.org/packages/68/6d/84ce50e7ee1ae79984d689e05a9937b2460d4efa1e5b202b46762fb9036c/pybase64-1.4.3-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30f2fd53efecbdde4bdca73a872a68dcb0d1bf8a4560c70a3e7746df973e1ef3", size = 71688, upload-time = "2025-12-06T13:24:42.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/57/6743e420416c3ff1b004041c85eb0ebd9c50e9cf05624664bfa1dc8b5625/pybase64-1.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0932b0c5cfa617091fd74f17d24549ce5de3628791998c94ba57be808078eeaf", size = 60040, upload-time = "2025-12-06T13:24:44.37Z" }, + { url = "https://files.pythonhosted.org/packages/3b/68/733324e28068a89119af2921ce548e1c607cc5c17d354690fc51c302e326/pybase64-1.4.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:acb61f5ab72bec808eb0d4ce8b87ec9f38d7d750cb89b1371c35eb8052a29f11", size = 56478, upload-time = "2025-12-06T13:24:45.815Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9e/f3f4aa8cfe3357a3cdb0535b78eb032b671519d3ecc08c58c4c6b72b5a91/pybase64-1.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2bc2d5bc15168f5c04c53bdfe5a1e543b2155f456ed1e16d7edce9ce73842021", size = 59463, upload-time = "2025-12-06T13:24:46.938Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d1/53286038e1f0df1cf58abcf4a4a91b0f74ab44539c2547b6c31001ddd054/pybase64-1.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8a7bc3cd23880bdca59758bcdd6f4ef0674f2393782763910a7466fab35ccb98", size = 60360, upload-time = "2025-12-06T13:24:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/00/9a/5cc6ce95db2383d27ff4d790b8f8b46704d360d701ab77c4f655bcfaa6a7/pybase64-1.4.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ad15acf618880d99792d71e3905b0e2508e6e331b76a1b34212fa0f11e01ad28", size = 54999, upload-time = "2025-12-06T13:24:49.547Z" }, + { url = "https://files.pythonhosted.org/packages/64/e7/c3c1d09c3d7ae79e3aa1358c6d912d6b85f29281e47aa94fc0122a415a2f/pybase64-1.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448158d417139cb4851200e5fee62677ae51f56a865d50cda9e0d61bda91b116", size = 58736, upload-time = "2025-12-06T13:24:50.641Z" }, + { url = "https://files.pythonhosted.org/packages/db/d5/0baa08e3d8119b15b588c39f0d39fd10472f0372e3c54ca44649cbefa256/pybase64-1.4.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9058c49b5a2f3e691b9db21d37eb349e62540f9f5fc4beabf8cbe3c732bead86", size = 52298, upload-time = "2025-12-06T13:24:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/00/87/fc6f11474a1de7e27cd2acbb8d0d7508bda3efa73dfe91c63f968728b2a3/pybase64-1.4.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ce561724f6522907a66303aca27dce252d363fcd85884972d348f4403ba3011a", size = 69049, upload-time = "2025-12-06T13:24:53.253Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/7fb5566f669ac18b40aa5fc1c438e24df52b843c1bdc5da47d46d4c1c630/pybase64-1.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:63316560a94ac449fe86cb8b9e0a13714c659417e92e26a5cbf085cd0a0c838d", size = 57952, upload-time = "2025-12-06T13:24:54.342Z" }, + { url = "https://files.pythonhosted.org/packages/de/cc/ceb949232dbbd3ec4ee0190d1df4361296beceee9840390a63df8bc31784/pybase64-1.4.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ecd796f2ac0be7b73e7e4e232b8c16422014de3295d43e71d2b19fd4a4f5368", size = 54484, upload-time = "2025-12-06T13:24:55.774Z" }, + { url = "https://files.pythonhosted.org/packages/a7/69/659f3c8e6a5d7b753b9c42a4bd9c42892a0f10044e9c7351a4148d413a33/pybase64-1.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d01e102a12fb2e1ed3dc11611c2818448626637857ec3994a9cf4809dfd23477", size = 56542, upload-time = "2025-12-06T13:24:57Z" }, + { url = "https://files.pythonhosted.org/packages/85/2c/29c9e6c9c82b72025f9676f9e82eb1fd2339ad038cbcbf8b9e2ac02798fc/pybase64-1.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ebff797a93c2345f22183f454fd8607a34d75eca5a3a4a969c1c75b304cee39d", size = 71045, upload-time = "2025-12-06T13:24:58.179Z" }, + { url = "https://files.pythonhosted.org/packages/b9/84/5a3dce8d7a0040a5c0c14f0fe1311cd8db872913fa04438071b26b0dac04/pybase64-1.4.3-cp314-cp314-win32.whl", hash = "sha256:28b2a1bb0828c0595dc1ea3336305cd97ff85b01c00d81cfce4f92a95fb88f56", size = 34200, upload-time = "2025-12-06T13:24:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/57/bc/ce7427c12384adee115b347b287f8f3cf65860b824d74fe2c43e37e81c1f/pybase64-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:33338d3888700ff68c3dedfcd49f99bfc3b887570206130926791e26b316b029", size = 36323, upload-time = "2025-12-06T13:25:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/9a/1b/2b8ffbe9a96eef7e3f6a5a7be75995eebfb6faaedc85b6da6b233e50c778/pybase64-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:62725669feb5acb186458da2f9353e88ae28ef66bb9c4c8d1568b12a790dfa94", size = 31584, upload-time = "2025-12-06T13:25:02.801Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/6824c2e6fb45b8fa4e7d92e3c6805432d5edc7b855e3e8e1eedaaf6efb7c/pybase64-1.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:153fe29be038948d9372c3e77ae7d1cab44e4ba7d9aaf6f064dbeea36e45b092", size = 38601, upload-time = "2025-12-06T13:25:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/ea/e5/10d2b3a4ad3a4850be2704a2f70cd9c0cf55725c8885679872d3bc846c67/pybase64-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7fe3decaa7c4a9e162327ec7bd81ce183d2b16f23c6d53b606649c6e0203e9e", size = 32078, upload-time = "2025-12-06T13:25:05.362Z" }, + { url = "https://files.pythonhosted.org/packages/43/04/8b15c34d3c2282f1c1b0850f1113a249401b618a382646a895170bc9b5e7/pybase64-1.4.3-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a5ae04ea114c86eb1da1f6e18d75f19e3b5ae39cb1d8d3cd87c29751a6a22780", size = 72474, upload-time = "2025-12-06T13:25:06.434Z" }, + { url = "https://files.pythonhosted.org/packages/42/00/f34b4d11278f8fdc68bc38f694a91492aa318f7c6f1bd7396197ac0f8b12/pybase64-1.4.3-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1755b3dce3a2a5c7d17ff6d4115e8bee4a1d5aeae74469db02e47c8f477147da", size = 75706, upload-time = "2025-12-06T13:25:07.636Z" }, + { url = "https://files.pythonhosted.org/packages/bb/5d/71747d4ad7fe16df4c4c852bdbdeb1f2cf35677b48d7c34d3011a7a6ad3a/pybase64-1.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb852f900e27ffc4ec1896817535a0fa19610ef8875a096b59f21d0aa42ff172", size = 65589, upload-time = "2025-12-06T13:25:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/49/b1/d1e82bd58805bb5a3a662864800bab83a83a36ba56e7e3b1706c708002a5/pybase64-1.4.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9cf21ea8c70c61eddab3421fbfce061fac4f2fb21f7031383005a1efdb13d0b9", size = 60670, upload-time = "2025-12-06T13:25:10.04Z" }, + { url = "https://files.pythonhosted.org/packages/15/67/16c609b7a13d1d9fc87eca12ba2dce5e67f949eeaab61a41bddff843cbb0/pybase64-1.4.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:afff11b331fdc27692fc75e85ae083340a35105cea1a3c4552139e2f0e0d174f", size = 64194, upload-time = "2025-12-06T13:25:11.48Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/37bc724e42960f0106c2d33dc957dcec8f760c91a908cc6c0df7718bc1a8/pybase64-1.4.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9a5143df542c1ce5c1f423874b948c4d689b3f05ec571f8792286197a39ba02", size = 64984, upload-time = "2025-12-06T13:25:12.645Z" }, + { url = "https://files.pythonhosted.org/packages/6e/66/b2b962a6a480dd5dae3029becf03ea1a650d326e39bf1c44ea3db78bb010/pybase64-1.4.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d62e9861019ad63624b4a7914dff155af1cc5d6d79df3be14edcaedb5fdad6f9", size = 58750, upload-time = "2025-12-06T13:25:13.848Z" }, + { url = "https://files.pythonhosted.org/packages/2b/15/9b6d711035e29b18b2e1c03d47f41396d803d06ef15b6c97f45b75f73f04/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84cfd4d92668ef5766cc42a9c9474b88960ac2b860767e6e7be255c6fddbd34a", size = 63816, upload-time = "2025-12-06T13:25:15.356Z" }, + { url = "https://files.pythonhosted.org/packages/b4/21/e2901381ed0df62e2308380f30d9c4d87d6b74e33a84faed3478d33a7197/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:60fc025437f9a7c2cc45e0c19ed68ed08ba672be2c5575fd9d98bdd8f01dd61f", size = 56348, upload-time = "2025-12-06T13:25:16.559Z" }, + { url = "https://files.pythonhosted.org/packages/c4/16/3d788388a178a0407aa814b976fe61bfa4af6760d9aac566e59da6e4a8b4/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edc8446196f04b71d3af76c0bd1fe0a45066ac5bffecca88adb9626ee28c266f", size = 72842, upload-time = "2025-12-06T13:25:18.055Z" }, + { url = "https://files.pythonhosted.org/packages/a6/63/c15b1f8bd47ea48a5a2d52a4ec61f037062932ea6434ab916107b58e861e/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e99f6fa6509c037794da57f906ade271f52276c956d00f748e5b118462021d48", size = 62651, upload-time = "2025-12-06T13:25:19.191Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b8/f544a2e37c778d59208966d4ef19742a0be37c12fc8149ff34483c176616/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d94020ef09f624d841aa9a3a6029df8cf65d60d7a6d5c8687579fa68bd679b65", size = 58295, upload-time = "2025-12-06T13:25:20.822Z" }, + { url = "https://files.pythonhosted.org/packages/03/99/1fae8a3b7ac181e36f6e7864a62d42d5b1f4fa7edf408c6711e28fba6b4d/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f64ce70d89942a23602dee910dec9b48e5edf94351e1b378186b74fcc00d7f66", size = 60960, upload-time = "2025-12-06T13:25:22.099Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9e/cd4c727742345ad8384569a4466f1a1428f4e5cc94d9c2ab2f53d30be3fe/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ea99f56e45c469818b9781903be86ba4153769f007ba0655fa3b46dc332803d", size = 74863, upload-time = "2025-12-06T13:25:23.442Z" }, + { url = "https://files.pythonhosted.org/packages/28/86/a236ecfc5b494e1e922da149689f690abc84248c7c1358f5605b8c9fdd60/pybase64-1.4.3-cp314-cp314t-win32.whl", hash = "sha256:343b1901103cc72362fd1f842524e3bb24978e31aea7ff11e033af7f373f66ab", size = 34513, upload-time = "2025-12-06T13:25:24.592Z" }, + { url = "https://files.pythonhosted.org/packages/56/ce/ca8675f8d1352e245eb012bfc75429ee9cf1f21c3256b98d9a329d44bf0f/pybase64-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:57aff6f7f9dea6705afac9d706432049642de5b01080d3718acc23af87c5af76", size = 36702, upload-time = "2025-12-06T13:25:25.72Z" }, + { url = "https://files.pythonhosted.org/packages/3b/30/4a675864877397179b09b720ee5fcb1cf772cf7bebc831989aff0a5f79c1/pybase64-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e906aa08d4331e799400829e0f5e4177e76a3281e8a4bc82ba114c6b30e405c9", size = 31904, upload-time = "2025-12-06T13:25:26.826Z" }, + { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, + { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, + { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, +] + +[[package]] +name = "pycountry" +version = "26.2.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1d/061b9e7a48b85cfd69f33c33d2ef784a531c359399ad764243399673c8f5/pycountry-26.2.16.tar.gz", hash = "sha256:5b6027d453fcd6060112b951dd010f01f168b51b4bf8a1f1fc8c95c8d94a0801", size = 7711342, upload-time = "2026-02-17T03:42:52.367Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/42/7703bd45b62fecd44cd7d3495423097e2f7d28bc2e99e7c1af68892ab157/pycountry-26.2.16-py3-none-any.whl", hash = "sha256:115c4baf7cceaa30f59a4694d79483c9167dbce7a9de4d3d571c5f3ea77c305a", size = 8044600, upload-time = "2026-02-17T03:42:49.777Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, +] + +[[package]] +name = "pydantic-extra-types" +version = "2.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/71/dba38ee2651f84f7842206adbd2233d8bbdb59fb85e9fa14232486a8c471/pydantic_extra_types-2.11.1.tar.gz", hash = "sha256:46792d2307383859e923d8fcefa82108b1a141f8a9c0198982b3832ab5ef1049", size = 172002, upload-time = "2026-03-16T08:08:03.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/c1/3226e6d7f5a4f736f38ac11a6fbb262d701889802595cdb0f53a885ac2e0/pydantic_extra_types-2.11.1-py3-none-any.whl", hash = "sha256:1722ea2bddae5628ace25f2aa685b69978ef533123e5638cfbddb999e0100ec1", size = 79526, upload-time = "2026-03-16T08:08:02.533Z" }, +] + +[package.optional-dependencies] +pycountry = [ + { name = "pycountry" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, +] + +[[package]] +name = "pydub" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326, upload-time = "2021-03-10T02:09:54.659Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327, upload-time = "2021-03-10T02:09:53.503Z" }, +] + +[[package]] +name = "pyelftools" +version = "0.33" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/11/767522582afab1b884d277de0e6e011640cb9d7292a38694b4b1a1df1ae8/pyelftools-0.33.tar.gz", hash = "sha256:660d82dcbeb8e83d1702bd97f223f761625da06111c0cc988eac6b8ab0c1b61f", size = 15068655, upload-time = "2026-05-29T12:56:22.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/2a/f9697576603dae937727827505a6126a066affb227034e77e6f9068910da/pyelftools-0.33-py3-none-any.whl", hash = "sha256:f215ad5f47d3f1373a21496a6c9e0707c622840d0622f23ff7ce08678b020036", size = 201178, upload-time = "2026-05-29T12:56:20.587Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-json-logger" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573, upload-time = "2026-03-29T04:39:56.805Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021, upload-time = "2026-03-29T04:39:55.266Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/82/c8cd43a6e0719bf5a3b034f6726dd701f75829c08944c83d4b95d02ed0e8/python_multipart-0.0.30.tar.gz", hash = "sha256:0edfe0475c1f46ddd3ff7785a626f6118af32bdcf359bb21260367313bb32118", size = 46316, upload-time = "2026-05-31T19:24:55.198Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/fd/0318007beb234790993d3ec5afd051d1dbceb733e81e3afe2b981ece3f37/python_multipart-0.0.30-py3-none-any.whl", hash = "sha256:830964def8c90607ac5daa00514e3987815865713ade8d20febc9177ac0c3c5b", size = 29730, upload-time = "2026-05-31T19:24:53.814Z" }, +] + +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "pyzmq" +version = "27.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" }, + { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" }, + { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" }, + { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" }, + { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" }, + { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" }, + { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" }, + { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" }, + { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" }, + { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" }, + { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" }, + { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" }, + { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" }, +] + +[[package]] +name = "quack-kernels" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "apache-tvm-ffi" }, + { name = "einops" }, + { name = "nvidia-cutlass-dsl" }, + { name = "torch" }, + { name = "torch-c-dlpack-ext" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/94/ee76e3a3dc74d986b7b24c5928f1d14b01bd5152375688c2ede369f6d19b/quack_kernels-0.5.0.tar.gz", hash = "sha256:c7c7338b67243397b6ca166e648bba161076e99f3858b532e1c877dcc6eaa03d", size = 366426, upload-time = "2026-05-29T05:00:25.985Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/2b/a8f171d5e172880885571bf89e93204aaf231a0e92c4c84714eaf18c271a/quack_kernels-0.5.0-py3-none-any.whl", hash = "sha256:08821ebfb8e638cc20308d5c59410c6dbb3b637ccc7b07bd57c7a9261a06af74", size = 327709, upload-time = "2026-05-29T05:00:24.679Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "regex" +version = "2026.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, + { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, + { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, + { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, + { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, + { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, + { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, + { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, + { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, + { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, + { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, + { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, + { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, + { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, + { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, + { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, + { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, + { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rich-rst" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/56/3191bae66b08ccc637ea8120426068bcb361cc323c96404c310886937067/rich_rst-2.0.1.tar.gz", hash = "sha256:cbe236ed0901d1ec8427cc6a50bf0a34353ba28ad014dc24def68bfe7f3b9e68", size = 300570, upload-time = "2026-05-16T00:47:57.362Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/3d/55c17d3ebdf3cd81356002afe5bef9bb8af631db2819785b6eac845b925b/rich_rst-2.0.1-py3-none-any.whl", hash = "sha256:7ee15f345ce25fa02b582c272a6cdbaf0c21243e38061cea273cff659bf3ef61", size = 272922, upload-time = "2026-05-16T00:47:55.508Z" }, +] + +[[package]] +name = "rich-toolkit" +version = "0.19.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/02/32217f3657ae91a0ea7cf1d74ade78f44352f830d00c468f753ddb3d4980/rich_toolkit-0.19.10.tar.gz", hash = "sha256:dc2e8c515ef9fbb4894e62bd41a2d2960dd7c2f505b5084894604d5ccfee3f09", size = 198167, upload-time = "2026-05-21T10:11:42.397Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/84/a005adcb4d1e6846ba3d62768090c3b943e3f6d8dc5c47af64f33584c4a7/rich_toolkit-0.19.10-py3-none-any.whl", hash = "sha256:93a41f67a09aefe90379f1729495c2fee9ccbcc8cfda48e2ca2ae54a995e32b1", size = 33907, upload-time = "2026-05-21T10:11:43.578Z" }, +] + +[[package]] +name = "rignore" +version = "0.7.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/0e/012556ef3047a2628842b44e753bb15f4dc46806780ff090f1e8fe4bf1eb/rignore-0.7.6-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:03e82348cb7234f8d9b2834f854400ddbbd04c0f8f35495119e66adbd37827a8", size = 883488, upload-time = "2025-11-05T20:42:41.359Z" }, + { url = "https://files.pythonhosted.org/packages/93/b0/d4f1f3fe9eb3f8e382d45ce5b0547ea01c4b7e0b4b4eb87bcd66a1d2b888/rignore-0.7.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9e624f6be6116ea682e76c5feb71ea91255c67c86cb75befe774365b2931961", size = 820411, upload-time = "2025-11-05T20:42:24.782Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c8/dea564b36dedac8de21c18e1851789545bc52a0c22ece9843444d5608a6a/rignore-0.7.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bda49950d405aa8d0ebe26af807c4e662dd281d926530f03f29690a2e07d649a", size = 897821, upload-time = "2025-11-05T20:40:52.613Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/ee96db17ac1835e024c5d0742eefb7e46de60020385ac883dd3d1cde2c1f/rignore-0.7.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5fd5ab3840b8c16851d327ed06e9b8be6459702a53e5ab1fc4073b684b3789e", size = 873963, upload-time = "2025-11-05T20:41:07.49Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8c/ad5a57bbb9d14d5c7e5960f712a8a0b902472ea3f4a2138cbf70d1777b75/rignore-0.7.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ced2a248352636a5c77504cb755dc02c2eef9a820a44d3f33061ce1bb8a7f2d2", size = 1169216, upload-time = "2025-11-05T20:41:23.73Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/5b00bc2a6bc1701e6878fca798cf5d9125eb3113193e33078b6fc0d99123/rignore-0.7.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a04a3b73b75ddc12c9c9b21efcdaab33ca3832941d6f1d67bffd860941cd448a", size = 942942, upload-time = "2025-11-05T20:41:39.393Z" }, + { url = "https://files.pythonhosted.org/packages/85/e5/7f99bd0cc9818a91d0e8b9acc65b792e35750e3bdccd15a7ee75e64efca4/rignore-0.7.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24321efac92140b7ec910ac7c53ab0f0c86a41133d2bb4b0e6a7c94967f44dd", size = 959787, upload-time = "2025-11-05T20:42:09.765Z" }, + { url = "https://files.pythonhosted.org/packages/55/54/2ffea79a7c1eabcede1926347ebc2a81bc6b81f447d05b52af9af14948b9/rignore-0.7.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c7aa109d41e593785c55fdaa89ad80b10330affa9f9d3e3a51fa695f739b20", size = 984245, upload-time = "2025-11-05T20:41:54.062Z" }, + { url = "https://files.pythonhosted.org/packages/41/f7/e80f55dfe0f35787fa482aa18689b9c8251e045076c35477deb0007b3277/rignore-0.7.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1734dc49d1e9501b07852ef44421f84d9f378da9fbeda729e77db71f49cac28b", size = 1078647, upload-time = "2025-11-05T21:40:13.463Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cf/2c64f0b6725149f7c6e7e5a909d14354889b4beaadddaa5fff023ec71084/rignore-0.7.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5719ea14ea2b652c0c0894be5dfde954e1853a80dea27dd2fbaa749618d837f5", size = 1139186, upload-time = "2025-11-05T21:40:31.27Z" }, + { url = "https://files.pythonhosted.org/packages/75/95/a86c84909ccc24af0d094b50d54697951e576c252a4d9f21b47b52af9598/rignore-0.7.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e23424fc7ce35726854f639cb7968151a792c0c3d9d082f7f67e0c362cfecca", size = 1117604, upload-time = "2025-11-05T21:40:48.07Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5e/13b249613fd5d18d58662490ab910a9f0be758981d1797789913adb4e918/rignore-0.7.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3efdcf1dd84d45f3e2bd2f93303d9be103888f56dfa7c3349b5bf4f0657ec696", size = 1127725, upload-time = "2025-11-05T21:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/c7/28/fa5dcd1e2e16982c359128664e3785f202d3eca9b22dd0b2f91c4b3d242f/rignore-0.7.6-cp312-cp312-win32.whl", hash = "sha256:ccca9d1a8b5234c76b71546fc3c134533b013f40495f394a65614a81f7387046", size = 646145, upload-time = "2025-11-05T21:41:51.096Z" }, + { url = "https://files.pythonhosted.org/packages/26/87/69387fb5dd81a0f771936381431780b8cf66fcd2cfe9495e1aaf41548931/rignore-0.7.6-cp312-cp312-win_amd64.whl", hash = "sha256:c96a285e4a8bfec0652e0bfcf42b1aabcdda1e7625f5006d188e3b1c87fdb543", size = 726090, upload-time = "2025-11-05T21:41:36.485Z" }, + { url = "https://files.pythonhosted.org/packages/24/5f/e8418108dcda8087fb198a6f81caadbcda9fd115d61154bf0df4d6d3619b/rignore-0.7.6-cp312-cp312-win_arm64.whl", hash = "sha256:a64a750e7a8277a323f01ca50b7784a764845f6cce2fe38831cb93f0508d0051", size = 656317, upload-time = "2025-11-05T21:41:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8a/a4078f6e14932ac7edb171149c481de29969d96ddee3ece5dc4c26f9e0c3/rignore-0.7.6-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2bdab1d31ec9b4fb1331980ee49ea051c0d7f7bb6baa28b3125ef03cdc48fdaf", size = 883057, upload-time = "2025-11-05T20:42:42.741Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8f/f8daacd177db4bf7c2223bab41e630c52711f8af9ed279be2058d2fe4982/rignore-0.7.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90f0a00ce0c866c275bf888271f1dc0d2140f29b82fcf33cdbda1e1a6af01010", size = 820150, upload-time = "2025-11-05T20:42:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/b65b837e39c3f7064c426754714ac633b66b8c2290978af9d7f513e14aa9/rignore-0.7.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ad295537041dc2ed4b540fb1a3906bd9ede6ccdad3fe79770cd89e04e3c73c", size = 897406, upload-time = "2025-11-05T20:40:53.854Z" }, + { url = "https://files.pythonhosted.org/packages/ca/58/1970ce006c427e202ac7c081435719a076c478f07b3a23f469227788dc23/rignore-0.7.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f782dbd3a65a5ac85adfff69e5c6b101285ef3f845c3a3cae56a54bebf9fe116", size = 874050, upload-time = "2025-11-05T20:41:08.922Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/eb45db9f90137329072a732273be0d383cb7d7f50ddc8e0bceea34c1dfdf/rignore-0.7.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65cece3b36e5b0826d946494734c0e6aaf5a0337e18ff55b071438efe13d559e", size = 1167835, upload-time = "2025-11-05T20:41:24.997Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f1/6f1d72ddca41a64eed569680587a1236633587cc9f78136477ae69e2c88a/rignore-0.7.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7e4bb66c13cd7602dc8931822c02dfbbd5252015c750ac5d6152b186f0a8be0", size = 941945, upload-time = "2025-11-05T20:41:40.628Z" }, + { url = "https://files.pythonhosted.org/packages/48/6f/2f178af1c1a276a065f563ec1e11e7a9e23d4996fd0465516afce4b5c636/rignore-0.7.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297e500c15766e196f68aaaa70e8b6db85fa23fdc075b880d8231fdfba738cd7", size = 959067, upload-time = "2025-11-05T20:42:11.09Z" }, + { url = "https://files.pythonhosted.org/packages/5b/db/423a81c4c1e173877c7f9b5767dcaf1ab50484a94f60a0b2ed78be3fa765/rignore-0.7.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a07084211a8d35e1a5b1d32b9661a5ed20669970b369df0cf77da3adea3405de", size = 984438, upload-time = "2025-11-05T20:41:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/31/eb/c4f92cc3f2825d501d3c46a244a671eb737fc1bcf7b05a3ecd34abb3e0d7/rignore-0.7.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:181eb2a975a22256a1441a9d2f15eb1292839ea3f05606620bd9e1938302cf79", size = 1078365, upload-time = "2025-11-05T21:40:15.148Z" }, + { url = "https://files.pythonhosted.org/packages/26/09/99442f02794bd7441bfc8ed1c7319e890449b816a7493b2db0e30af39095/rignore-0.7.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7bbcdc52b5bf9f054b34ce4af5269df5d863d9c2456243338bc193c28022bd7b", size = 1139066, upload-time = "2025-11-05T21:40:32.771Z" }, + { url = "https://files.pythonhosted.org/packages/2c/88/bcfc21e520bba975410e9419450f4b90a2ac8236b9a80fd8130e87d098af/rignore-0.7.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f2e027a6da21a7c8c0d87553c24ca5cc4364def18d146057862c23a96546238e", size = 1118036, upload-time = "2025-11-05T21:40:49.646Z" }, + { url = "https://files.pythonhosted.org/packages/e2/25/d37215e4562cda5c13312636393aea0bafe38d54d4e0517520a4cc0753ec/rignore-0.7.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee4a18b82cbbc648e4aac1510066682fe62beb5dc88e2c67c53a83954e541360", size = 1127550, upload-time = "2025-11-05T21:41:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/dc/76/a264ab38bfa1620ec12a8ff1c07778da89e16d8c0f3450b0333020d3d6dc/rignore-0.7.6-cp313-cp313-win32.whl", hash = "sha256:a7d7148b6e5e95035d4390396895adc384d37ff4e06781a36fe573bba7c283e5", size = 646097, upload-time = "2025-11-05T21:41:53.201Z" }, + { url = "https://files.pythonhosted.org/packages/62/44/3c31b8983c29ea8832b6082ddb1d07b90379c2d993bd20fce4487b71b4f4/rignore-0.7.6-cp313-cp313-win_amd64.whl", hash = "sha256:b037c4b15a64dced08fc12310ee844ec2284c4c5c1ca77bc37d0a04f7bff386e", size = 726170, upload-time = "2025-11-05T21:41:38.131Z" }, + { url = "https://files.pythonhosted.org/packages/aa/41/e26a075cab83debe41a42661262f606166157df84e0e02e2d904d134c0d8/rignore-0.7.6-cp313-cp313-win_arm64.whl", hash = "sha256:e47443de9b12fe569889bdbe020abe0e0b667516ee2ab435443f6d0869bd2804", size = 656184, upload-time = "2025-11-05T21:41:27.396Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b9/1f5bd82b87e5550cd843ceb3768b4a8ef274eb63f29333cf2f29644b3d75/rignore-0.7.6-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8e41be9fa8f2f47239ded8920cc283699a052ac4c371f77f5ac017ebeed75732", size = 882632, upload-time = "2025-11-05T20:42:44.063Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6b/07714a3efe4a8048864e8a5b7db311ba51b921e15268b17defaebf56d3db/rignore-0.7.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6dc1e171e52cefa6c20e60c05394a71165663b48bca6c7666dee4f778f2a7d90", size = 820760, upload-time = "2025-11-05T20:42:27.885Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0f/348c829ea2d8d596e856371b14b9092f8a5dfbb62674ec9b3f67e4939a9d/rignore-0.7.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce2268837c3600f82ab8db58f5834009dc638ee17103582960da668963bebc5", size = 899044, upload-time = "2025-11-05T20:40:55.336Z" }, + { url = "https://files.pythonhosted.org/packages/f0/30/2e1841a19b4dd23878d73edd5d82e998a83d5ed9570a89675f140ca8b2ad/rignore-0.7.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:690a3e1b54bfe77e89c4bacb13f046e642f8baadafc61d68f5a726f324a76ab6", size = 874144, upload-time = "2025-11-05T20:41:10.195Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bf/0ce9beb2e5f64c30e3580bef09f5829236889f01511a125f98b83169b993/rignore-0.7.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09d12ac7a0b6210c07bcd145007117ebd8abe99c8eeb383e9e4673910c2754b2", size = 1168062, upload-time = "2025-11-05T20:41:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/b9/8b/571c178414eb4014969865317da8a02ce4cf5241a41676ef91a59aab24de/rignore-0.7.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a2b2b74a8c60203b08452479b90e5ce3dbe96a916214bc9eb2e5af0b6a9beb0", size = 942542, upload-time = "2025-11-05T20:41:41.838Z" }, + { url = "https://files.pythonhosted.org/packages/19/62/7a3cf601d5a45137a7e2b89d10c05b5b86499190c4b7ca5c3c47d79ee519/rignore-0.7.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc5a531ef02131e44359419a366bfac57f773ea58f5278c2cdd915f7d10ea94", size = 958739, upload-time = "2025-11-05T20:42:12.463Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1f/4261f6a0d7caf2058a5cde2f5045f565ab91aa7badc972b57d19ce58b14e/rignore-0.7.6-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7a1f77d9c4cd7e76229e252614d963442686bfe12c787a49f4fe481df49e7a9", size = 984138, upload-time = "2025-11-05T20:41:56.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/bf/628dfe19c75e8ce1f45f7c248f5148b17dfa89a817f8e3552ab74c3ae812/rignore-0.7.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ead81f728682ba72b5b1c3d5846b011d3e0174da978de87c61645f2ed36659a7", size = 1079299, upload-time = "2025-11-05T21:40:16.639Z" }, + { url = "https://files.pythonhosted.org/packages/af/a5/be29c50f5c0c25c637ed32db8758fdf5b901a99e08b608971cda8afb293b/rignore-0.7.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:12ffd50f520c22ffdabed8cd8bfb567d9ac165b2b854d3e679f4bcaef11a9441", size = 1139618, upload-time = "2025-11-05T21:40:34.507Z" }, + { url = "https://files.pythonhosted.org/packages/2a/40/3c46cd7ce4fa05c20b525fd60f599165e820af66e66f2c371cd50644558f/rignore-0.7.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e5a16890fbe3c894f8ca34b0fcacc2c200398d4d46ae654e03bc9b3dbf2a0a72", size = 1117626, upload-time = "2025-11-05T21:40:51.494Z" }, + { url = "https://files.pythonhosted.org/packages/8c/b9/aea926f263b8a29a23c75c2e0d8447965eb1879d3feb53cfcf84db67ed58/rignore-0.7.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3abab3bf99e8a77488ef6c7c9a799fac22224c28fe9f25cc21aa7cc2b72bfc0b", size = 1128144, upload-time = "2025-11-05T21:41:09.169Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f6/0d6242f8d0df7f2ecbe91679fefc1f75e7cd2072cb4f497abaab3f0f8523/rignore-0.7.6-cp314-cp314-win32.whl", hash = "sha256:eeef421c1782953c4375aa32f06ecae470c1285c6381eee2a30d2e02a5633001", size = 646385, upload-time = "2025-11-05T21:41:55.105Z" }, + { url = "https://files.pythonhosted.org/packages/d5/38/c0dcd7b10064f084343d6af26fe9414e46e9619c5f3224b5272e8e5d9956/rignore-0.7.6-cp314-cp314-win_amd64.whl", hash = "sha256:6aeed503b3b3d5af939b21d72a82521701a4bd3b89cd761da1e7dc78621af304", size = 725738, upload-time = "2025-11-05T21:41:39.736Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7a/290f868296c1ece914d565757ab363b04730a728b544beb567ceb3b2d96f/rignore-0.7.6-cp314-cp314-win_arm64.whl", hash = "sha256:104f215b60b3c984c386c3e747d6ab4376d5656478694e22c7bd2f788ddd8304", size = 656008, upload-time = "2025-11-05T21:41:29.028Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d2/3c74e3cd81fe8ea08a8dcd2d755c09ac2e8ad8fe409508904557b58383d3/rignore-0.7.6-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bb24a5b947656dd94cb9e41c4bc8b23cec0c435b58be0d74a874f63c259549e8", size = 882835, upload-time = "2025-11-05T20:42:45.443Z" }, + { url = "https://files.pythonhosted.org/packages/77/61/a772a34b6b63154877433ac2d048364815b24c2dd308f76b212c408101a2/rignore-0.7.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b1e33c9501cefe24b70a1eafd9821acfd0ebf0b35c3a379430a14df089993e3", size = 820301, upload-time = "2025-11-05T20:42:29.226Z" }, + { url = "https://files.pythonhosted.org/packages/71/30/054880b09c0b1b61d17eeb15279d8bf729c0ba52b36c3ada52fb827cbb3c/rignore-0.7.6-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bec3994665a44454df86deb762061e05cd4b61e3772f5b07d1882a8a0d2748d5", size = 897611, upload-time = "2025-11-05T20:40:56.475Z" }, + { url = "https://files.pythonhosted.org/packages/1e/40/b2d1c169f833d69931bf232600eaa3c7998ba4f9a402e43a822dad2ea9f2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26cba2edfe3cff1dfa72bddf65d316ddebf182f011f2f61538705d6dbaf54986", size = 873875, upload-time = "2025-11-05T20:41:11.561Z" }, + { url = "https://files.pythonhosted.org/packages/55/59/ca5ae93d83a1a60e44b21d87deb48b177a8db1b85e82fc8a9abb24a8986d/rignore-0.7.6-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffa86694fec604c613696cb91e43892aa22e1fec5f9870e48f111c603e5ec4e9", size = 1167245, upload-time = "2025-11-05T20:41:28.29Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/cf3dce392ba2af806cba265aad6bcd9c48bb2a6cb5eee448d3319f6e505b/rignore-0.7.6-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48efe2ed95aa8104145004afb15cdfa02bea5cdde8b0344afeb0434f0d989aa2", size = 941750, upload-time = "2025-11-05T20:41:43.111Z" }, + { url = "https://files.pythonhosted.org/packages/ec/be/3f344c6218d779395e785091d05396dfd8b625f6aafbe502746fcd880af2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dcae43eb44b7f2457fef7cc87f103f9a0013017a6f4e62182c565e924948f21", size = 958896, upload-time = "2025-11-05T20:42:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/c9/34/d3fa71938aed7d00dcad87f0f9bcb02ad66c85d6ffc83ba31078ce53646a/rignore-0.7.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2cd649a7091c0dad2f11ef65630d30c698d505cbe8660dd395268e7c099cc99f", size = 983992, upload-time = "2025-11-05T20:41:58.022Z" }, + { url = "https://files.pythonhosted.org/packages/24/a4/52a697158e9920705bdbd0748d59fa63e0f3233fb92e9df9a71afbead6ca/rignore-0.7.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42de84b0289d478d30ceb7ae59023f7b0527786a9a5b490830e080f0e4ea5aeb", size = 1078181, upload-time = "2025-11-05T21:40:18.151Z" }, + { url = "https://files.pythonhosted.org/packages/ac/65/aa76dbcdabf3787a6f0fd61b5cc8ed1e88580590556d6c0207960d2384bb/rignore-0.7.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:875a617e57b53b4acbc5a91de418233849711c02e29cc1f4f9febb2f928af013", size = 1139232, upload-time = "2025-11-05T21:40:35.966Z" }, + { url = "https://files.pythonhosted.org/packages/08/44/31b31a49b3233c6842acc1c0731aa1e7fb322a7170612acf30327f700b44/rignore-0.7.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8703998902771e96e49968105207719f22926e4431b108450f3f430b4e268b7c", size = 1117349, upload-time = "2025-11-05T21:40:53.013Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ae/1b199a2302c19c658cf74e5ee1427605234e8c91787cfba0015f2ace145b/rignore-0.7.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:602ef33f3e1b04c1e9a10a3c03f8bc3cef2d2383dcc250d309be42b49923cabc", size = 1127702, upload-time = "2025-11-05T21:41:10.881Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d3/18210222b37e87e36357f7b300b7d98c6dd62b133771e71ae27acba83a4f/rignore-0.7.6-cp314-cp314t-win32.whl", hash = "sha256:c1d8f117f7da0a4a96a8daef3da75bc090e3792d30b8b12cfadc240c631353f9", size = 647033, upload-time = "2025-11-05T21:42:00.095Z" }, + { url = "https://files.pythonhosted.org/packages/3e/87/033eebfbee3ec7d92b3bb1717d8f68c88e6fc7de54537040f3b3a405726f/rignore-0.7.6-cp314-cp314t-win_amd64.whl", hash = "sha256:ca36e59408bec81de75d307c568c2d0d410fb880b1769be43611472c61e85c96", size = 725647, upload-time = "2025-11-05T21:41:44.449Z" }, + { url = "https://files.pythonhosted.org/packages/79/62/b88e5879512c55b8ee979c666ee6902adc4ed05007226de266410ae27965/rignore-0.7.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b83adabeb3e8cf662cabe1931b83e165b88c526fa6af6b3aa90429686e474896", size = 656035, upload-time = "2025-11-05T21:41:31.13Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/43/25a8dcd3feedd735039a8f0b5b7e3b118232b5eae288c4fd9ab200d41094/rpds_py-2026.5.1.tar.gz", hash = "sha256:07b24fea40541e28570e5b795a4a38fbdcd12550c06bd0748005ecc8116ca256", size = 64459, upload-time = "2026-05-28T12:02:13.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/e7/a78582dc57caa592dcc7d4fb69b61390561e908eb3d2f5df5928a8e354c0/rpds_py-2026.5.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3abe24a66e57adcfa645d718063a5fa5103ecc71ddbf26d78af8f9368018ff1d", size = 353040, upload-time = "2026-05-28T11:59:12.531Z" }, + { url = "https://files.pythonhosted.org/packages/a3/43/35e3f136343aef451e545ce8c38d36c2f93c0ed88703db8b64ba2b205c68/rpds_py-2026.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b1d94308ddf0b1982f61f2eb54bf92997c9ece8a8093ef014250f4a517906c", size = 345775, upload-time = "2026-05-28T11:59:13.827Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/0f2160c5982d3157734d5cb3ed63d8b2d583a73c9864f77b666449f32cf8/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa92420128dadce7f54bd73ba1825a273e9268fe9e35dbf7e6362890efa4e08", size = 376329, upload-time = "2026-05-28T11:59:15.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/11/ee0ba42aff83bf4effdbc576673c6be64c5e173978c3f6d537e94482f77d/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca653c6546386227cd9800d1bef6a348099acf8db4250341da6d90f663d6dfcb", size = 383539, upload-time = "2026-05-28T11:59:16.665Z" }, + { url = "https://files.pythonhosted.org/packages/11/df/d94aa6a499d4ac40afe2d7620f2c597fd3c0f182e854ad7cf3f596a81cb6/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66c93681c4729e4e3ecba31b8179fae083ff3118841672835140338b4b9867c1", size = 494674, upload-time = "2026-05-28T11:59:17.991Z" }, + { url = "https://files.pythonhosted.org/packages/1f/75/33d30f43bb2f458de11979486a591b1bf6e5651765ed1704c6197c2dc773/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40ff257542e04796880e011e15cd4dc21c2599975df2aaa8f2c8495ca574e1a5", size = 389268, upload-time = "2026-05-28T11:59:19.434Z" }, + { url = "https://files.pythonhosted.org/packages/f4/1e/2c9096fc19d5fd084b0184ca2b651e659aa0a37e6fdbecf6ece47f147fe1/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6825cc329b290e93c5f6a9be2393118a763f6ccf6abd83704e0c102ca583644", size = 376280, upload-time = "2026-05-28T11:59:21Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e5/61ec9f8be8211ea7f48448195549e4aaf02004083475493b0e137702ecb2/rpds_py-2026.5.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:de42116e69cb53b911cc34aee5ab98f36c597b822545045d49e938818b99e5e4", size = 387233, upload-time = "2026-05-28T11:59:22.454Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ca/bcec1005c4f4a234f92a29078631fee49206c7265ccae966f18fd332e80e/rpds_py-2026.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0f920015df2a504bebaba6d4c31ccf3fcf942f92655c086da30b671aad19aa6", size = 405009, upload-time = "2026-05-28T11:59:23.845Z" }, + { url = "https://files.pythonhosted.org/packages/72/e6/4d5718c5cf26c522dc7c9999e238da1e77380b81d0c5d1df11e271ddfeb1/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0408a24e44feb919423dc6d9da677cb5cddb894d2ca9e763967d156d9c60fab4", size = 553113, upload-time = "2026-05-28T11:59:25.184Z" }, + { url = "https://files.pythonhosted.org/packages/d4/25/2ee807bdb3e1f0b7eddf7782acd5665a8b5205a331a7d7244a52c4812fd9/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cea68bcd53467561ae2f96a6bdad1544299ba97b5b0ddcd5ac3d376e5c781c24", size = 618838, upload-time = "2026-05-28T11:59:26.749Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c1/7d4c26f167f8c41501cc073d30ee22082b16ce358cf5b00ec97cbc7804ea/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4be8b1d2a705cc37d08256004e1d07de143fa0075c8e85a3df020b776f62b732", size = 582436, upload-time = "2026-05-28T11:59:28.11Z" }, + { url = "https://files.pythonhosted.org/packages/04/1d/9d12b0a337bab46f4769f8857f4007e3b2d639e14f9a44a0efe157696e64/rpds_py-2026.5.1-cp312-cp312-win32.whl", hash = "sha256:6736718bd4fc49cbcb538ba30516fdbef161522acefb739657d48b97bd864fed", size = 212734, upload-time = "2026-05-28T11:59:29.689Z" }, + { url = "https://files.pythonhosted.org/packages/c5/93/e4116f2de7f56bc7406a76033dc501811ddeb22b7f056b92d632871ebb0c/rpds_py-2026.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:0a7d1eec967df0e9b22614a5e177622e0c89611d03727fa0cb48e45028907870", size = 229045, upload-time = "2026-05-28T11:59:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/53/6c3419d85eb2ec5938a37627c585b42d76a63bb731d6e42ed4b079ebf486/rpds_py-2026.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1841d067089e117142d79b98aa0df2f08b52f2ecc1819dd2700636c0db74a473", size = 223967, upload-time = "2026-05-28T11:59:32.318Z" }, + { url = "https://files.pythonhosted.org/packages/6c/32/14c961ad295f490eb0849ada8b79683e93a59b9de3afdd983eaf55fa6867/rpds_py-2026.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:efef4ac29c6ff495531eb17ee705b62841ecaa291b7c7077e848ea03e237164d", size = 352787, upload-time = "2026-05-28T11:59:33.655Z" }, + { url = "https://files.pythonhosted.org/packages/ca/bb/d1b85117967c11191441a7274ae616c65d93901d082c588f89a50a8da5ae/rpds_py-2026.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c39f5b67a8a2e67179ada2a954227d670fe65fa9098457f698f56ddf248709b3", size = 345179, upload-time = "2026-05-28T11:59:35Z" }, + { url = "https://files.pythonhosted.org/packages/7c/46/d84105f062e626a1b233f863907288a4708c2d833b8b4c6fb2764bc080c0/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5c30f3f04eef4fbd362226a6f31d7c8895ca4fbb6e0b790f6890a98d8da8559", size = 376173, upload-time = "2026-05-28T11:59:36.43Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ae/469d7959ce5b1201e1de135dc735b86db3b35dd0d1734f6a44246d5f061c/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:277f6c82f0580848796c7ecc8a7173aa3bfb928e4ff831261c2f60a81dc270db", size = 383162, upload-time = "2026-05-28T11:59:37.995Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a2/57853d31a1116a561aa072794602ad3f6341e18d70a8523f1bd5b9fc1e5a/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63c2c4c213f1a4e3f3de28ecab029dbdee976324e729c0d7a55211be72576b02", size = 495093, upload-time = "2026-05-28T11:59:39.453Z" }, + { url = "https://files.pythonhosted.org/packages/99/63/3a8eabcad9314b7daf5c65f451d2c33d989235cd8a5762186cf2c3f5a4f8/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3350ec808fb538fe71a1f94dfaa0e29c598dfad805ce49f0caec5ae3183c652b", size = 389829, upload-time = "2026-05-28T11:59:40.896Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/05678d97fc25e2622df14dc530fb82023174ecfff6733991ed0d78f167bd/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b964e3ab599e718dc46c018d104b1ebc007cbc6567d827c94a687fca56d77e", size = 374786, upload-time = "2026-05-28T11:59:42.626Z" }, + { url = "https://files.pythonhosted.org/packages/88/d1/8c90b6431e80a3b91b284a5c7c8c0c4f9c006444d90477a740d6e0f9c694/rpds_py-2026.5.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:19cb09fab7b7fc96b2a6e28f2e34b72a3705ff27b37edb77455316e5d3f3dc9b", size = 386920, upload-time = "2026-05-28T11:59:44.124Z" }, + { url = "https://files.pythonhosted.org/packages/ff/99/4638f672ab356682d633ee0da9255f5b67ce6efd0b85eb94ad3e255e65a5/rpds_py-2026.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abe76bcdba31e576cb83eeb8797aa0d882b738fef6dc65d0601fc753806a5b46", size = 405059, upload-time = "2026-05-28T11:59:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/66/3f/3546524b6eb4cc2e1f363a3d638fa52f6c24faae3500c25fb488b02f1740/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8bff7073db3899158fff55ebf57b113a67030af26f80a18978f9f0aa60250ddf", size = 553030, upload-time = "2026-05-28T11:59:48.603Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c3/7b3388c796fcf471bd17194242d4dc1a7608567c0fa422bcc1c5e79f9c1e/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8ba264fa49be666cd9cc56bf34ec7002fb3d27a4aee5bcb4d43d0d18feb1bb6f", size = 618975, upload-time = "2026-05-28T11:59:50.314Z" }, + { url = "https://files.pythonhosted.org/packages/61/1e/a3cb07f2795075d1d88efddae2f541359fde5f08c81ee114c29c2949c90a/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4860b603ddda0475a8885499b3729e90229d480105b42651962a5397d995fa89", size = 581178, upload-time = "2026-05-28T11:59:51.673Z" }, + { url = "https://files.pythonhosted.org/packages/a1/74/e758c03a5ef46f04c37f2651a2893db846d569ba8a7bca469d4b58939bcd/rpds_py-2026.5.1-cp313-cp313-win32.whl", hash = "sha256:7944270ae71383f6e2657dd7d5ce4eeb4ac2d0059a6738f0510583d462ab4842", size = 212481, upload-time = "2026-05-28T11:59:53.148Z" }, + { url = "https://files.pythonhosted.org/packages/70/ec/a2aca432db9c7359b40fa393eeeaa0d166c2f70175be956e75fa24197c44/rpds_py-2026.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:88647f43a73c4e01be19b04ceef0c8d3a1958153604d13c773becd8016f2a0cf", size = 228519, upload-time = "2026-05-28T11:59:54.505Z" }, + { url = "https://files.pythonhosted.org/packages/29/60/a73bfdd45b096574556acf303bbd9fa9eed36ca8a818b514e2a5d5fe2b9d/rpds_py-2026.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:453895624ecf7db7063b1004e44037522bbaef9ff6a945e59bc71662d7a03abd", size = 223446, upload-time = "2026-05-28T11:59:56.081Z" }, + { url = "https://files.pythonhosted.org/packages/18/e2/408105fd611823f00882aea810f3989a30d26b1bab8b6beb20f98c724e0e/rpds_py-2026.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4e4bc98639ec915f512fde3aa7a95e0041d95d9c3cc86eea841fa63cb1e8600", size = 355287, upload-time = "2026-05-28T11:59:57.448Z" }, + { url = "https://files.pythonhosted.org/packages/8d/58/5c4a43436843c90d0f6d19f82c200c80e3843ca9fa07b237623327f6d384/rpds_py-2026.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cacedb7a6e167680acba45ad5716e89067d225dc80da0d7040cae8c81d4572fa", size = 347033, upload-time = "2026-05-28T11:59:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c2/1a71acdacaf4e259b10278fb87b039ded3cf80041bcd89dd8a3ea702ded6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68700371c5d7ae1412862ddfa719090925c93ecf351c566d66f09d04b136ea00", size = 376891, upload-time = "2026-05-28T12:00:00.516Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c8/535f3d9b65addd8e28aa87b83c6e526799c3717a88273db8ea795beeef7a/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296c799becfa849c779c8725494fe9ed94959ed886787df4364b058465bad7f0", size = 385646, upload-time = "2026-05-28T12:00:02.394Z" }, + { url = "https://files.pythonhosted.org/packages/1c/91/dc033f313345c354ade914dbe73cdb90b615a4409ea02430d5356794f3d8/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3858b908218ee108d0bbfb2095ccc237648053c9bf98affad7cb079acaf1d97", size = 498830, upload-time = "2026-05-28T12:00:04.189Z" }, + { url = "https://files.pythonhosted.org/packages/27/fc/90fcbea459dbb8ddc18a2e0fd1de9412b48bc84ffff2db771cf714bacfd6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fb8d2e7cb2f850b169806d61d1b991738acec96500a75c30f49caf064ce7cef", size = 392830, upload-time = "2026-05-28T12:00:05.797Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1d/46cd11a228c9750684a798d98f878be6f614aa762438da7378f035e79e35/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b74c10ed6a8f190f4287f53bcfea348b92a84a9c9f70d30183d1e6172d580d", size = 379613, upload-time = "2026-05-28T12:00:07.433Z" }, + { url = "https://files.pythonhosted.org/packages/24/4a/d9b0c6af3a1de03eb93741bbe8be2bdce84d8fda8224f3005451d86df389/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b9a6528956191c48c52294a592dbd4a8386d7048bdb25c0efcb6b966466c6d83", size = 388183, upload-time = "2026-05-28T12:00:09.227Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b4/db7aaabdda6d020afc87d981bcc2f57a434c7dec60ecfc2ab3dd50b20351/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:af03e34e860047bc7a352b842856fcf78798fbb81132cc98bd2f907ab4eb9cd2", size = 408578, upload-time = "2026-05-28T12:00:10.779Z" }, + { url = "https://files.pythonhosted.org/packages/08/d6/070f6a41cbb343e2ac4171859bf3f3623e0ab002f72619d6d505313ec2de/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fea6e836d10abbe191d557d33bd58bd5987725fe63aa1eefe557d230209855bd", size = 553573, upload-time = "2026-05-28T12:00:12.443Z" }, + { url = "https://files.pythonhosted.org/packages/75/ab/1a71ea3589c4345dac0a0518f0e6a031cb42689277851b683c46d27463a5/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:fc0c0f878ea770a0a8a462456c5ad36fc9fe6358e6b76fdadc7f17575e0b8bf1", size = 620861, upload-time = "2026-05-28T12:00:14.09Z" }, + { url = "https://files.pythonhosted.org/packages/8a/22/9bf80a56069c0c443fcfefac639a86a744550a2898817a6dfd3e26654924/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e0b360f316d966b048b085857630b3cc51f3db2f07b06f440eac8f695374d1e3", size = 585633, upload-time = "2026-05-28T12:00:15.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/3b2c0a75c9e04125696f84ebdbbf304acf5a40b58ba4481cdb98a922c3ba/rpds_py-2026.5.1-cp313-cp313t-win32.whl", hash = "sha256:a2999883eedf72fdfb7520b92c7d4ec2572a71ff40239377aa604cc529eecafc", size = 210074, upload-time = "2026-05-28T12:00:17.291Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8b/609157d5a25d37d4f29f92840ba531f416907c34ae5c5739dd21fc2bef98/rpds_py-2026.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e07be2a9d7122bd6e82dea89814ef8dc893feb1aae97fec1630f3263bbb30e55", size = 228635, upload-time = "2026-05-28T12:00:18.73Z" }, + { url = "https://files.pythonhosted.org/packages/d4/6f/19c1918a4b590d8de87e712e4abe4b3875771eff60216fb6153cf6665c68/rpds_py-2026.5.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1f2c391c3059798093b65df23aca2cac150460ae9c630d99dec83d703d9485b9", size = 349756, upload-time = "2026-05-28T12:00:20.217Z" }, + { url = "https://files.pythonhosted.org/packages/e5/60/a06fe7da34eca79dacbf958a2ba0c6eea85bc2b29de20080bf40f72f66fa/rpds_py-2026.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:413b424f7c4ee65ab5e5be91f5731be0f8b41a1ee2b12dfe810d716312e95a78", size = 343831, upload-time = "2026-05-28T12:00:21.711Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ec/b2333b97b90e2a6ef6ca8ad386ee284968e74bcfe113b3f1a8d9036429a9/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c595a1d9255dce0599e13130d1440ab2506654f2b50294226ee06402f8fef63", size = 375127, upload-time = "2026-05-28T12:00:23.326Z" }, + { url = "https://files.pythonhosted.org/packages/14/7f/e00aae54067f2b488c4637961d5f58204d470795fc791085fa3f15060d2e/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c27c5f6102eac8c03e7595a00827a53b271ba40a53b59ff8709170e0855ea4a", size = 379034, upload-time = "2026-05-28T12:00:24.89Z" }, + { url = "https://files.pythonhosted.org/packages/be/cc/423999bbb8ae8dc93c77fc1d5e984ade5eb89d237d3bb884ccfa72ae2890/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c7fcf61d44cacecaf3aea542b0e053db77972a4573e7ceda16fb2b399161195", size = 490823, upload-time = "2026-05-28T12:00:26.676Z" }, + { url = "https://files.pythonhosted.org/packages/0f/aa/c671bf660f12e68d3c52ff86c7066ed1372df5a0f4f2ff584e419b8207e7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c817a189d4ee14290420e5ff051e4dd6baa13f3edf84685071dee07a6d538ee", size = 388144, upload-time = "2026-05-28T12:00:28.577Z" }, + { url = "https://files.pythonhosted.org/packages/19/c8/d63bb75b68afe77b229e3021c6031bcaf01da5db5b0e69d0d10f9ba679a7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21846aac0ed2e0589f38c12dc44e77bb64e494b771eadbcf169cba00566ba7ba", size = 371959, upload-time = "2026-05-28T12:00:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/82/35/c51122014d8274ff37dc606d60049c3db7d83da02b5b282511e5a906a9a6/rpds_py-2026.5.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b317c87a13f769a4e787819bd508aaa5d69aa09b0880de9af6d3a8a54571cdec", size = 383558, upload-time = "2026-05-28T12:00:31.764Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f9/2790cb99c136a5363acdeacf5c27c56f3de0d4118a1f48fca83404c99c89/rpds_py-2026.5.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce87129d9f2c14fa6c4a8601fb80eb4488c80d38a20cd13758ef11123e14995d", size = 402789, upload-time = "2026-05-28T12:00:33.247Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1b/e4fb584f8c75d35c38150ff6a332cda949e6f97acba1f4fd123b14ab56fe/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9cdddb6c1207d284d94fd1530adf57fbd797fe7c4b8704ba85f49414f2557e7d", size = 551405, upload-time = "2026-05-28T12:00:34.819Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f7/a6731b4216cb3793ea1af5391da240f5683dacc0d13e034fe5fc3503f240/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4e237e139f94d3c036fd28eb9f564c99055476ff4ff05cd42be55ce349b5aa02", size = 616975, upload-time = "2026-05-28T12:00:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/2e051a81d95d8e63f4b35a1c463a87e8766bc3d083c067c5dfb6bf220747/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ed0954b524873214369184a9c82b0eaa45a3fbb9a798cd95b17e0d98499e7ea0", size = 578701, upload-time = "2026-05-28T12:00:37.82Z" }, + { url = "https://files.pythonhosted.org/packages/65/56/b5f6fdb2083e32bca8a8993d89e70db114b4756c9e2c38421328126689d2/rpds_py-2026.5.1-cp314-cp314-win32.whl", hash = "sha256:2d88621d6a7d4dfa633d21abe90f280bb205274e16b1d1e61c6ad4640b2453b7", size = 209806, upload-time = "2026-05-28T12:00:39.492Z" }, + { url = "https://files.pythonhosted.org/packages/fb/80/65a5aa96c155e611d1ed844e4e1f57f3e36b021f396d9f8585d756e6b90d/rpds_py-2026.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:cef8ac28d26f4dda3533060c20fbf80a325458fa9fd23ea72a73cdfa8e978838", size = 225985, upload-time = "2026-05-28T12:00:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/27/7c/ad185212e87b05f196daef92bc5f3caf07298eb47c295b5585c3dd3093ac/rpds_py-2026.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:eaaea962c68cdc68d4a533ba985ab8e9484277910bbfaa2ab3ef7732667bfed8", size = 221219, upload-time = "2026-05-28T12:00:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/23/58/e14ae18759020334646b031e708ab4158d653a938822bfb7b95ef2e93aa3/rpds_py-2026.5.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:21942f52dbbd5f8758bf021213d28bd45c39e873e65e2407faf5f1846f5761ad", size = 352148, upload-time = "2026-05-28T12:00:44.638Z" }, + { url = "https://files.pythonhosted.org/packages/31/9b/5f4a1e2f960bca3ac5d052b139dd31eed97b259f9d909173821760d542e8/rpds_py-2026.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f414556f6e3958300ff941e40c9f97e3dc9774ddd1b3434c475d73dd354bbed3", size = 345196, upload-time = "2026-05-28T12:00:46.14Z" }, + { url = "https://files.pythonhosted.org/packages/1a/71/1d9574d6a2fa20ab60eaa55c7467f5aa20cbc770f341a05f09c0876f59e2/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1013a8625c74043210190b246f5b1551e09757c1f356c6e4160ef96c5bc081", size = 374981, upload-time = "2026-05-28T12:00:47.531Z" }, + { url = "https://files.pythonhosted.org/packages/0c/9a/37e99f4915a80aa71670263c1267f7ae0af95f53a3f61e6c3bdc016d4515/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc68e231a77a5f0d774ae278a1f8e55c0456501820847c1e4efb3829f3441df6", size = 379961, upload-time = "2026-05-28T12:00:49.216Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ff/6e73f74b89d2e0715e0fc86b7dde893f9a61ae2f9b256ff3bdfe41ac4e94/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9baffb505aff33acc69b422a19f77806680f3c8632227d79f48de8a810d1c2c5", size = 495965, upload-time = "2026-05-28T12:00:51.111Z" }, + { url = "https://files.pythonhosted.org/packages/ea/e0/425faba25f59d74d4638b267f7c7a80e8649d2ef4db10a19b0c4a71e6e6f/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8d2f912928d426e8cfa396f7f3f8d29a59e6689c86dcca3c420730c1096322b", size = 389526, upload-time = "2026-05-28T12:00:52.77Z" }, + { url = "https://files.pythonhosted.org/packages/c6/76/7a41960e3fddae47fab43a28684d5da981401dffd88253de0944148654cb/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90f628283be835db980c941767d41c9a27b5239e54ba0a9c1335247e82406964", size = 376190, upload-time = "2026-05-28T12:00:54.215Z" }, + { url = "https://files.pythonhosted.org/packages/27/60/5f38dc70824fc6951b51d35377e577a3a3a4c81a6769cc5a2de25ebe0ad1/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:1ebb2f0ab7e16132995a72de805170e0203df0c3dd22e1ef1cd1fdd90bd7a131", size = 383921, upload-time = "2026-05-28T12:00:55.673Z" }, + { url = "https://files.pythonhosted.org/packages/60/1a/d60a38caa1505f4b9483c3fbbde12c94e1079154f4f401a6da96f7e77621/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f3df3d16ded76f1f8c9cdebd0e1ea55fdf4c23b812de189814da7cf229c22a81", size = 404766, upload-time = "2026-05-28T12:00:57.518Z" }, + { url = "https://files.pythonhosted.org/packages/87/ff/602fd3f174d6425f0bce05ad0dfbec0e96b38d0f7d08a79af5aa20083885/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9af8905b8f854990e40d5206aa5ac58d9b0fe0b7f351ff2bb086c20f6c8c6a47", size = 551343, upload-time = "2026-05-28T12:00:58.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c1/1be13327acdbead3eca1fde03b6a34dbb011f1e864e217f0d32cc1779a7f/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:036a36a87fb1cd3b214d11c4b3c4f7d2ddad933625dca1c900b56a057c07740a", size = 618502, upload-time = "2026-05-28T12:01:00.656Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d7/afb49b49d7f2be8b7ba1a9f0977fa5168003437b93086726f066544e8351/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ae3853454fe9ef283a03c96c2d835d39e84b14643a9d62c82ef0fb87d702ca", size = 581916, upload-time = "2026-05-28T12:01:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/25/d1/dbef8c1f8a10f07beb62b5f054e20099fd9924b3ec001b8f0b6ac7813a85/rpds_py-2026.5.1-cp314-cp314t-win32.whl", hash = "sha256:6c3d771a46ec18b12af06ce36243a9a80b07a5d0515236332d90863ca8bb326a", size = 207855, upload-time = "2026-05-28T12:01:03.821Z" }, + { url = "https://files.pythonhosted.org/packages/2a/72/bfa4e61ab8e7dc1c8adf397e05e6cbdd4239357bd72b248d3de662f23915/rpds_py-2026.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c93c629be4636cf54337bd5f06c104d55e42ced54d681f6fe21ae510a65116f6", size = 225422, upload-time = "2026-05-28T12:01:05.194Z" }, + { url = "https://files.pythonhosted.org/packages/27/3a/7b5da92b640f67b6717ccafc83cdd06bfa7ff2395c3685c68922bb54d703/rpds_py-2026.5.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:3574b55c604b8f75dacb007136508bbc0db406e626301778096a133327e7f2fb", size = 349576, upload-time = "2026-05-28T12:01:06.722Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8a/2aafd7ad355a1bd48ca76e2262b74b15e6432b5a1efe150efd4d779cd55d/rpds_py-2026.5.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:94068eb3ae6d43f5a786b7db96a406a34e6d5c24489feef32fd6e8946ea7b291", size = 343640, upload-time = "2026-05-28T12:01:08.441Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7d/6c9523c1abbe840a1b7fba3c516d48e1d3487cc80fea4366c4071cf56784/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a5b10e8ce894825f380a8f1b6444cf73c294dfea62afbb2d13e3a9e630cec1", size = 375322, upload-time = "2026-05-28T12:01:09.934Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5d/0b7b03fb1dc509321f01de3149784ab773e34c8573022029af8076afcb9c/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc09f82e63d4bcd58149572f857a431bae851dc747e313c3b5bdf7abb907fda8", size = 379066, upload-time = "2026-05-28T12:01:11.48Z" }, + { url = "https://files.pythonhosted.org/packages/d7/e2/8ef6012999ebf1cb1c22f876d9ce5e63d960fd4631d2af3202d3f480aa25/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e10464d17df3b582745c25cec695cb9558bca2cb6ddb631aee1787fc72c767b2", size = 494586, upload-time = "2026-05-28T12:01:13.051Z" }, + { url = "https://files.pythonhosted.org/packages/80/af/1eeb029bec67582c226b7809172207cd005073af4ebd906e65ff494f4983/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba05adbf15d994c38ec0b7ab32e858e5110c21e9009a00a86545fd220f84e038", size = 388415, upload-time = "2026-05-28T12:01:14.631Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/ffbe10711c4d766c1cab0557d6906c074f795814863c67b351355d29354a/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77c004fdc7b891967106f78ddfd7b076bfe6813c6139c6fff6aed3bcaa960b26", size = 372427, upload-time = "2026-05-28T12:01:16.153Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3a/30ba4a6ad457e5b070c18d742a33fb77d8d922b565cc881f8a5313d63bfe/rpds_py-2026.5.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:83bcf894486c9d78dd290d3c0124ff6dd8875d3025e2090a8ec49fcc37c55fdd", size = 383615, upload-time = "2026-05-28T12:01:17.809Z" }, + { url = "https://files.pythonhosted.org/packages/d3/69/62e242b53ce39c0814bd24e1a6e6eba6c92be716277745f317f9540a2e7b/rpds_py-2026.5.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3df104083952a0e0c6f10de33e440eabe98fb6317d23e1a58c68f6df08d01b9", size = 402786, upload-time = "2026-05-28T12:01:19.419Z" }, + { url = "https://files.pythonhosted.org/packages/38/c1/a770b9c186928a1ed0f7e6d7ae50e7f3950ed23e3f9e366dbc8e38cb55de/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:980450826cf22e133c57e0835070bdd0dd3f73b9b708c3ce223def2cb9469e14", size = 551583, upload-time = "2026-05-28T12:01:21.013Z" }, + { url = "https://files.pythonhosted.org/packages/21/7c/68e8579b95375b70d2a963103c42e705856cdb98569258bd807f4423891c/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:205dde846f24332ab0c1188699a043b8d165b79bb84529ce272c45048ff6be01", size = 616941, upload-time = "2026-05-28T12:01:22.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/a1/a6135aed5730ff03ab957182259987ac11e55fb392a28dc6f0592048a280/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3966b82dd563176396df030f3dd52a6e54cb69b718e95e78bd555ed3d1e0185d", size = 578349, upload-time = "2026-05-28T12:01:24.118Z" }, + { url = "https://files.pythonhosted.org/packages/09/6e/f24201a76a84e6c49d0bdfdfcb735210e21701e9b21c5bfc0ba497dd62f6/rpds_py-2026.5.1-cp315-cp315-win32.whl", hash = "sha256:7818f8d0a415be74d2be3590b0a1c1f463a642f4d0217e7d10602dceef5b79aa", size = 209922, upload-time = "2026-05-28T12:01:25.522Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e4/966bc240bb0485fc265278f6de44d05834bf0b3618886e0b22e33d54c49a/rpds_py-2026.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:b3cc20c0d800af78fd0fac68086e28c1856cec51ea528bb81ea851aa40d39325", size = 226003, upload-time = "2026-05-28T12:01:27.062Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5c/a15a59269cd5e74472734516c73795c15eccfc841b3d4b0228c3f53f19d0/rpds_py-2026.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:3609e9939a8a76cd904cf98a3f1f13b5dc7e150adeaee89e0ea09652ea213e16", size = 221245, upload-time = "2026-05-28T12:01:28.51Z" }, + { url = "https://files.pythonhosted.org/packages/e0/22/135ce03804e179a71ceb13be095deda4a279bc88f7a6b8fa161c5ad44e12/rpds_py-2026.5.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:5d333a7127d4b307601ac37792bee01bb95c867cbfacf21b6375b804d6bbd723", size = 352015, upload-time = "2026-05-28T12:01:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5f/f1f6d2652eb9d848f6eb369d8db83a2da6249bb49ad2c2a48f45d54538d3/rpds_py-2026.5.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:b5f077b44a4f7808520f66dae234988d867deb9aed9be5da057ce9ba831b2a41", size = 345016, upload-time = "2026-05-28T12:01:31.656Z" }, + { url = "https://files.pythonhosted.org/packages/88/66/b74182775691ea2290c99e52ac8d5db844e56fbec90ce421f107658c8314/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d8f9b7b78c9538fc9e04e82ec0e888ff0c3cffcfad152c77e57cd09351a98a", size = 374775, upload-time = "2026-05-28T12:01:33.136Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8f/15e5a61d9f0a43902d36561d4f07cae6ae9f4716be825159fd72717f33af/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e3a8ae58895ac107ed934a6bf51e5846f95c53b9b940c2c6d310838fd5846358", size = 380270, upload-time = "2026-05-28T12:01:34.574Z" }, + { url = "https://files.pythonhosted.org/packages/02/c3/f859b12763a80540cdf2af0f15b19904cf756a71d7bdd3f82ff3e5b1bbf9/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0957cf3c2b8632ec7aaebffebea8005b353cc2a237b6e2ae3c2cac0820704cfb", size = 495285, upload-time = "2026-05-28T12:01:36.127Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c7/ff27c2ac8411d30b03b1829fd88cae8dad1a4d0da48dd25e57c4038042e6/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c396c1304de421050b3681ea70f371874b54d41b0151e96109758144c231e30b", size = 389581, upload-time = "2026-05-28T12:01:37.635Z" }, + { url = "https://files.pythonhosted.org/packages/6e/67/fe92ee32a6cc05c77228a2f8b1762e7124f386ec20ff83d0757b762d58d0/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad1bff7f666b9598e573815affd666aac6a13a585dde336f843e33350c7fadc", size = 376041, upload-time = "2026-05-28T12:01:39.307Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/b4d6685c27aba55bd82f25b278be8237038117d05f9659a6213ad3408130/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:656a042550878f12d45752452d47094b7cfe5ad1e9d7b87b5a22ad3ae5ff8015", size = 383946, upload-time = "2026-05-28T12:01:41.043Z" }, + { url = "https://files.pythonhosted.org/packages/bd/79/2c1d832a53c8e0f8e98fc970ec257b950fecd4f62be2ab7182b500a0cbc8/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c4bd4f70294737b5206a3e8e30ccadbf8a60301831c8ea23eec5dbeea1ecfa", size = 405526, upload-time = "2026-05-28T12:01:43.032Z" }, + { url = "https://files.pythonhosted.org/packages/78/c4/c98117b03c6a8581ab2c2dfccfe9a5ad82bd8128a3c28b46a6ad2d97c393/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:43bca78665423cabae77146f2fe7ce55272b6c8d55d82cca83effd42c7e13972", size = 551165, upload-time = "2026-05-28T12:01:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c1/bc479ca069200af730881b1bd525e3114b2b391a351509fcb1b772f28086/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:42d0f20e85e549c870749d0e247f0c10d318a45b7e9676d575d2dcb04a1b2e66", size = 618778, upload-time = "2026-05-28T12:01:46.337Z" }, + { url = "https://files.pythonhosted.org/packages/77/65/38ab2f90df44c2febfb63cc10ced40763d9b4bc94d173e734528663fe7f5/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b1be5c35683684d5331b93600c210e8367c254683d8a6df6bd21bd2da3a334fb", size = 581839, upload-time = "2026-05-28T12:01:48.109Z" }, + { url = "https://files.pythonhosted.org/packages/15/2d/ce1f605fe036aadd460e5822e578c6c7ec3a860936cca37d6e0f299daa77/rpds_py-2026.5.1-cp315-cp315t-win32.whl", hash = "sha256:75808f6c38ce7749bb68cc2770161aae5045e6c6f6781a9782e74b93304399df", size = 207866, upload-time = "2026-05-28T12:01:49.648Z" }, + { url = "https://files.pythonhosted.org/packages/79/cb/966040123eb102371559746908ef2c9471f4d43e17ec9a645a2258dab64b/rpds_py-2026.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:90bd6630002a1c7f09e7843dd79f0d24f3d2897cc25a753480917865d14f15b3", size = 225441, upload-time = "2026-05-28T12:01:51.408Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/6f/a76f7d96e5c962f5b69cee865e49c15c1116897c01990faa8a57edb62e7f/ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6", size = 4706985, upload-time = "2026-05-28T14:16:57.784Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652, upload-time = "2026-05-28T14:16:06.701Z" }, + { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615, upload-time = "2026-05-28T14:16:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683, upload-time = "2026-05-28T14:16:40.974Z" }, + { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064, upload-time = "2026-05-28T14:16:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987, upload-time = "2026-05-28T14:16:43.732Z" }, + { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100, upload-time = "2026-05-28T14:16:55.244Z" }, + { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903, upload-time = "2026-05-28T14:16:14.368Z" }, + { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550, upload-time = "2026-05-28T14:16:20.138Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027, upload-time = "2026-05-28T14:16:46.615Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041, upload-time = "2026-05-28T14:16:26.247Z" }, + { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795, upload-time = "2026-05-28T14:16:17.11Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117, upload-time = "2026-05-28T14:16:31.767Z" }, + { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867, upload-time = "2026-05-28T14:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101, upload-time = "2026-05-28T14:16:49.598Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774, upload-time = "2026-05-28T14:16:52.337Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463, upload-time = "2026-05-28T14:16:11.333Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498, upload-time = "2026-05-28T14:16:38.425Z" }, +] + +[[package]] +name = "safehttpx" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/d1/4282284d9cf1ee873607a46442da977fc3c985059315ab23610be31d5885/safehttpx-0.1.7.tar.gz", hash = "sha256:db201c0978c41eddb8bb480f3eee59dd67304fdd91646035e9d9a720049a9d23", size = 10385, upload-time = "2025-10-24T18:30:09.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/a3/0f0b7d78e2f1eb9e8e1afbff1d2bff8d60144aee17aca51c065b516743dd/safehttpx-0.1.7-py3-none-any.whl", hash = "sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde", size = 8959, upload-time = "2025-10-24T18:30:08.733Z" }, +] + +[[package]] +name = "safetensors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, +] + +[[package]] +name = "secretstorage" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "jeepney", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, +] + +[[package]] +name = "semantic-version" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/31/f2289ce78b9b473d582568c234e104d2a342fd658cc288a7553d83bb8595/semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c", size = 52289, upload-time = "2022-05-26T13:35:23.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552, upload-time = "2022-05-26T13:35:21.206Z" }, +] + +[[package]] +name = "sentencepiece" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/15/2e7a025fc62d764b151ae6d0f2a92f8081755ebe8d4a64099accc6f77ba6/sentencepiece-0.2.1.tar.gz", hash = "sha256:8138cec27c2f2282f4a34d9a016e3374cd40e5c6e9cb335063db66a0a3b71fad", size = 3228515, upload-time = "2025-08-12T07:00:51.718Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/be/32ce495aa1d0e0c323dcb1ba87096037358edee539cac5baf8755a6bd396/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57cae326c8727de58c85977b175af132a7138d84c764635d7e71bbee7e774133", size = 1943152, upload-time = "2025-08-12T06:59:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/88/7e/ff23008899a58678e98c6ff592bf4d368eee5a71af96d0df6b38a039dd4f/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:56dd39a3c4d6493db3cdca7e8cc68c6b633f0d4195495cbadfcf5af8a22d05a6", size = 1325651, upload-time = "2025-08-12T06:59:41.536Z" }, + { url = "https://files.pythonhosted.org/packages/19/84/42eb3ce4796777a1b5d3699dfd4dca85113e68b637f194a6c8d786f16a04/sentencepiece-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9381351182ff9888cc80e41c632e7e274b106f450de33d67a9e8f6043da6f76", size = 1253645, upload-time = "2025-08-12T06:59:42.903Z" }, + { url = "https://files.pythonhosted.org/packages/89/fa/d3d5ebcba3cb9e6d3775a096251860c41a6bc53a1b9461151df83fe93255/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99f955df238021bf11f0fc37cdb54fd5e5b5f7fd30ecc3d93fb48b6815437167", size = 1316273, upload-time = "2025-08-12T06:59:44.476Z" }, + { url = "https://files.pythonhosted.org/packages/04/88/14f2f4a2b922d8b39be45bf63d79e6cd3a9b2f248b2fcb98a69b12af12f5/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdfecef430d985f1c2bcbfff3defd1d95dae876fbd0173376012d2d7d24044b", size = 1387881, upload-time = "2025-08-12T06:59:46.09Z" }, + { url = "https://files.pythonhosted.org/packages/fd/b8/903e5ccb77b4ef140605d5d71b4f9e0ad95d456d6184688073ed11712809/sentencepiece-0.2.1-cp312-cp312-win32.whl", hash = "sha256:a483fd29a34c3e34c39ac5556b0a90942bec253d260235729e50976f5dba1068", size = 999540, upload-time = "2025-08-12T06:59:48.023Z" }, + { url = "https://files.pythonhosted.org/packages/2d/81/92df5673c067148c2545b1bfe49adfd775bcc3a169a047f5a0e6575ddaca/sentencepiece-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4cdc7c36234fda305e85c32949c5211faaf8dd886096c7cea289ddc12a2d02de", size = 1054671, upload-time = "2025-08-12T06:59:49.895Z" }, + { url = "https://files.pythonhosted.org/packages/fe/02/c5e3bc518655d714622bec87d83db9cdba1cd0619a4a04e2109751c4f47f/sentencepiece-0.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:daeb5e9e9fcad012324807856113708614d534f596d5008638eb9b40112cd9e4", size = 1033923, upload-time = "2025-08-12T06:59:51.952Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4a/85fbe1706d4d04a7e826b53f327c4b80f849cf1c7b7c5e31a20a97d8f28b/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dcd8161eee7b41aae57ded06272905dbd680a0a04b91edd0f64790c796b2f706", size = 1943150, upload-time = "2025-08-12T06:59:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/c2/83/4cfb393e287509fc2155480b9d184706ef8d9fa8cbf5505d02a5792bf220/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c6c8f42949f419ff8c7e9960dbadcfbc982d7b5efc2f6748210d3dd53a7de062", size = 1325651, upload-time = "2025-08-12T06:59:55.073Z" }, + { url = "https://files.pythonhosted.org/packages/8d/de/5a007fb53b1ab0aafc69d11a5a3dd72a289d5a3e78dcf2c3a3d9b14ffe93/sentencepiece-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:097f3394e99456e9e4efba1737c3749d7e23563dd1588ce71a3d007f25475fff", size = 1253641, upload-time = "2025-08-12T06:59:56.562Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d2/f552be5928105588f4f4d66ee37dd4c61460d8097e62d0e2e0eec41bc61d/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7b670879c370d350557edabadbad1f6561a9e6968126e6debca4029e5547820", size = 1316271, upload-time = "2025-08-12T06:59:58.109Z" }, + { url = "https://files.pythonhosted.org/packages/96/df/0cfe748ace5485be740fed9476dee7877f109da32ed0d280312c94ec259f/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7f0fd2f2693309e6628aeeb2e2faf6edd221134dfccac3308ca0de01f8dab47", size = 1387882, upload-time = "2025-08-12T07:00:00.701Z" }, + { url = "https://files.pythonhosted.org/packages/ac/dd/f7774d42a881ced8e1739f393ab1e82ece39fc9abd4779e28050c2e975b5/sentencepiece-0.2.1-cp313-cp313-win32.whl", hash = "sha256:92b3816aa2339355fda2c8c4e021a5de92180b00aaccaf5e2808972e77a4b22f", size = 999541, upload-time = "2025-08-12T07:00:02.709Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e9/932b9eae6fd7019548321eee1ab8d5e3b3d1294df9d9a0c9ac517c7b636d/sentencepiece-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:10ed3dab2044c47f7a2e7b4969b0c430420cdd45735d78c8f853191fa0e3148b", size = 1054669, upload-time = "2025-08-12T07:00:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/c9/3a/76488a00ea7d6931689cda28726a1447d66bf1a4837943489314593d5596/sentencepiece-0.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac650534e2251083c5f75dde4ff28896ce7c8904133dc8fef42780f4d5588fcd", size = 1033922, upload-time = "2025-08-12T07:00:06.496Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b6/08fe2ce819e02ccb0296f4843e3f195764ce9829cbda61b7513f29b95718/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8dd4b477a7b069648d19363aad0cab9bad2f4e83b2d179be668efa672500dc94", size = 1946052, upload-time = "2025-08-12T07:00:08.136Z" }, + { url = "https://files.pythonhosted.org/packages/ab/d9/1ea0e740591ff4c6fc2b6eb1d7510d02f3fb885093f19b2f3abd1363b402/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c0f672da370cc490e4c59d89e12289778310a0e71d176c541e4834759e1ae07", size = 1327408, upload-time = "2025-08-12T07:00:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/99/7e/1fb26e8a21613f6200e1ab88824d5d203714162cf2883248b517deb500b7/sentencepiece-0.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad8493bea8432dae8d6830365352350f3b4144415a1d09c4c8cb8d30cf3b6c3c", size = 1254857, upload-time = "2025-08-12T07:00:11.021Z" }, + { url = "https://files.pythonhosted.org/packages/bc/85/c72fd1f3c7a6010544d6ae07f8ddb38b5e2a7e33bd4318f87266c0bbafbf/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b81a24733726e3678d2db63619acc5a8dccd074f7aa7a54ecd5ca33ca6d2d596", size = 1315722, upload-time = "2025-08-12T07:00:12.989Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e8/661e5bd82a8aa641fd6c1020bd0e890ef73230a2b7215ddf9c8cd8e941c2/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a81799d0a68d618e89063fb423c3001a034c893069135ffe51fee439ae474d6", size = 1387452, upload-time = "2025-08-12T07:00:15.088Z" }, + { url = "https://files.pythonhosted.org/packages/99/5e/ae66c361023a470afcbc1fbb8da722c72ea678a2fcd9a18f1a12598c7501/sentencepiece-0.2.1-cp313-cp313t-win32.whl", hash = "sha256:89a3ea015517c42c0341d0d962f3e6aaf2cf10d71b1932d475c44ba48d00aa2b", size = 1002501, upload-time = "2025-08-12T07:00:16.966Z" }, + { url = "https://files.pythonhosted.org/packages/c1/03/d332828c4ff764e16c1b56c2c8f9a33488bbe796b53fb6b9c4205ddbf167/sentencepiece-0.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:33f068c9382dc2e7c228eedfd8163b52baa86bb92f50d0488bf2b7da7032e484", size = 1057555, upload-time = "2025-08-12T07:00:18.573Z" }, + { url = "https://files.pythonhosted.org/packages/88/14/5aee0bf0864df9bd82bd59e7711362908e4935e3f9cdc1f57246b5d5c9b9/sentencepiece-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:b3616ad246f360e52c85781e47682d31abfb6554c779e42b65333d4b5f44ecc0", size = 1036042, upload-time = "2025-08-12T07:00:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/24/9c/89eb8b2052f720a612478baf11c8227dcf1dc28cd4ea4c0c19506b5af2a2/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5d0350b686c320068702116276cfb26c066dc7e65cfef173980b11bb4d606719", size = 1943147, upload-time = "2025-08-12T07:00:21.809Z" }, + { url = "https://files.pythonhosted.org/packages/82/0b/a1432bc87f97c2ace36386ca23e8bd3b91fb40581b5e6148d24b24186419/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c7f54a31cde6fa5cb030370566f68152a742f433f8d2be458463d06c208aef33", size = 1325624, upload-time = "2025-08-12T07:00:23.289Z" }, + { url = "https://files.pythonhosted.org/packages/ea/99/bbe054ebb5a5039457c590e0a4156ed073fb0fe9ce4f7523404dd5b37463/sentencepiece-0.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c83b85ab2d6576607f31df77ff86f28182be4a8de6d175d2c33ca609925f5da1", size = 1253670, upload-time = "2025-08-12T07:00:24.69Z" }, + { url = "https://files.pythonhosted.org/packages/19/ad/d5c7075f701bd97971d7c2ac2904f227566f51ef0838dfbdfdccb58cd212/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1855f57db07b51fb51ed6c9c452f570624d2b169b36f0f79ef71a6e6c618cd8b", size = 1316247, upload-time = "2025-08-12T07:00:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/fb/03/35fbe5f3d9a7435eebd0b473e09584bd3cc354ce118b960445b060d33781/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01e6912125cb45d3792f530a4d38f8e21bf884d6b4d4ade1b2de5cf7a8d2a52b", size = 1387894, upload-time = "2025-08-12T07:00:28.339Z" }, + { url = "https://files.pythonhosted.org/packages/dc/aa/956ef729aafb6c8f9c443104c9636489093bb5c61d6b90fc27aa1a865574/sentencepiece-0.2.1-cp314-cp314-win32.whl", hash = "sha256:c415c9de1447e0a74ae3fdb2e52f967cb544113a3a5ce3a194df185cbc1f962f", size = 1096698, upload-time = "2025-08-12T07:00:29.764Z" }, + { url = "https://files.pythonhosted.org/packages/b8/cb/fe400d8836952cc535c81a0ce47dc6875160e5fedb71d2d9ff0e9894c2a6/sentencepiece-0.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:881b2e44b14fc19feade3cbed314be37de639fc415375cefaa5bc81a4be137fd", size = 1155115, upload-time = "2025-08-12T07:00:32.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/89/047921cf70f36c7b6b6390876b2399b3633ab73b8d0cb857e5a964238941/sentencepiece-0.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:2005242a16d2dc3ac5fe18aa7667549134d37854823df4c4db244752453b78a8", size = 1133890, upload-time = "2025-08-12T07:00:34.763Z" }, + { url = "https://files.pythonhosted.org/packages/a1/11/5b414b9fae6255b5fb1e22e2ed3dc3a72d3a694e5703910e640ac78346bb/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a19adcec27c524cb7069a1c741060add95f942d1cbf7ad0d104dffa0a7d28a2b", size = 1946081, upload-time = "2025-08-12T07:00:36.97Z" }, + { url = "https://files.pythonhosted.org/packages/77/eb/7a5682bb25824db8545f8e5662e7f3e32d72a508fdce086029d89695106b/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e37e4b4c4a11662b5db521def4e44d4d30ae69a1743241412a93ae40fdcab4bb", size = 1327406, upload-time = "2025-08-12T07:00:38.669Z" }, + { url = "https://files.pythonhosted.org/packages/03/b0/811dae8fb9f2784e138785d481469788f2e0d0c109c5737372454415f55f/sentencepiece-0.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:477c81505db072b3ab627e7eab972ea1025331bd3a92bacbf798df2b75ea86ec", size = 1254846, upload-time = "2025-08-12T07:00:40.611Z" }, + { url = "https://files.pythonhosted.org/packages/ef/23/195b2e7ec85ebb6a547969f60b723c7aca5a75800ece6cc3f41da872d14e/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:010f025a544ef770bb395091d57cb94deb9652d8972e0d09f71d85d5a0816c8c", size = 1315721, upload-time = "2025-08-12T07:00:42.914Z" }, + { url = "https://files.pythonhosted.org/packages/7e/aa/553dbe4178b5f23eb28e59393dddd64186178b56b81d9b8d5c3ff1c28395/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:733e59ff1794d26db706cd41fc2d7ca5f6c64a820709cb801dc0ea31780d64ab", size = 1387458, upload-time = "2025-08-12T07:00:44.56Z" }, + { url = "https://files.pythonhosted.org/packages/66/7c/08ff0012507297a4dd74a5420fdc0eb9e3e80f4e88cab1538d7f28db303d/sentencepiece-0.2.1-cp314-cp314t-win32.whl", hash = "sha256:d3233770f78e637dc8b1fda2cd7c3b99ec77e7505041934188a4e7fe751de3b0", size = 1099765, upload-time = "2025-08-12T07:00:46.058Z" }, + { url = "https://files.pythonhosted.org/packages/91/d5/2a69e1ce15881beb9ddfc7e3f998322f5cedcd5e4d244cb74dade9441663/sentencepiece-0.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e4366c97b68218fd30ea72d70c525e6e78a6c0a88650f57ac4c43c63b234a9d", size = 1157807, upload-time = "2025-08-12T07:00:47.673Z" }, + { url = "https://files.pythonhosted.org/packages/f3/16/54f611fcfc2d1c46cbe3ec4169780b2cfa7cf63708ef2b71611136db7513/sentencepiece-0.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:105e36e75cbac1292642045458e8da677b2342dcd33df503e640f0b457cb6751", size = 1136264, upload-time = "2025-08-12T07:00:49.485Z" }, +] + +[[package]] +name = "sentry-sdk" +version = "2.61.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/4d/3c66e6045bd2071256b6b6fdcb0cc02b86ce54b2acc2ceac79af8e0efbb5/sentry_sdk-2.61.0.tar.gz", hash = "sha256:1ca9b4bb777eb5be67004edab7eb894f21c6301f1d05ed64966719ad5d1764ce", size = 458510, upload-time = "2026-05-28T09:40:28.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/5a/9794736d5802689c1a48862e6afe6b7f3e86cc37c15d4a84bc0143877dc1/sentry_sdk-2.61.0-py3-none-any.whl", hash = "sha256:ec4d30273909cb1d198e03208b16ee70e2bc5d90a16fd9f1fb2fc6a72e1f03dc", size = 483111, upload-time = "2026-05-28T09:40:27.027Z" }, +] + +[[package]] +name = "setproctitle" +version = "1.3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" }, + { url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" }, + { url = "https://files.pythonhosted.org/packages/50/22/cee06af4ffcfb0e8aba047bd44f5262e644199ae7527ae2c1f672b86495c/setproctitle-1.3.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1", size = 33736, upload-time = "2025-09-05T12:49:40.565Z" }, + { url = "https://files.pythonhosted.org/packages/5c/00/a5949a8bb06ef5e7df214fc393bb2fb6aedf0479b17214e57750dfdd0f24/setproctitle-1.3.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6", size = 35605, upload-time = "2025-09-05T12:49:42.362Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3a/50caca532a9343828e3bf5778c7a84d6c737a249b1796d50dd680290594d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c", size = 33143, upload-time = "2025-09-05T12:49:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ca/14/b843a251296ce55e2e17c017d6b9f11ce0d3d070e9265de4ecad948b913d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a", size = 34434, upload-time = "2025-09-05T12:49:45.31Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b7/06145c238c0a6d2c4bc881f8be230bb9f36d2bf51aff7bddcb796d5eed67/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739", size = 32795, upload-time = "2025-09-05T12:49:46.419Z" }, + { url = "https://files.pythonhosted.org/packages/ef/dc/ef76a81fac9bf27b84ed23df19c1f67391a753eed6e3c2254ebcb5133f56/setproctitle-1.3.7-cp312-cp312-win32.whl", hash = "sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f", size = 12552, upload-time = "2025-09-05T12:49:47.635Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5b/a9fe517912cd6e28cf43a212b80cb679ff179a91b623138a99796d7d18a0/setproctitle-1.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300", size = 13247, upload-time = "2025-09-05T12:49:49.16Z" }, + { url = "https://files.pythonhosted.org/packages/5d/2f/fcedcade3b307a391b6e17c774c6261a7166aed641aee00ed2aad96c63ce/setproctitle-1.3.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c3736b2a423146b5e62230502e47e08e68282ff3b69bcfe08a322bee73407922", size = 18047, upload-time = "2025-09-05T12:49:50.271Z" }, + { url = "https://files.pythonhosted.org/packages/23/ae/afc141ca9631350d0a80b8f287aac79a76f26b6af28fd8bf92dae70dc2c5/setproctitle-1.3.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3384e682b158d569e85a51cfbde2afd1ab57ecf93ea6651fe198d0ba451196ee", size = 13073, upload-time = "2025-09-05T12:49:51.46Z" }, + { url = "https://files.pythonhosted.org/packages/87/ed/0a4f00315bc02510395b95eec3d4aa77c07192ee79f0baae77ea7b9603d8/setproctitle-1.3.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0564a936ea687cd24dffcea35903e2a20962aa6ac20e61dd3a207652401492dd", size = 33284, upload-time = "2025-09-05T12:49:52.741Z" }, + { url = "https://files.pythonhosted.org/packages/fc/e4/adf3c4c0a2173cb7920dc9df710bcc67e9bcdbf377e243b7a962dc31a51a/setproctitle-1.3.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5d1cb3f81531f0eb40e13246b679a1bdb58762b170303463cb06ecc296f26d0", size = 34104, upload-time = "2025-09-05T12:49:54.416Z" }, + { url = "https://files.pythonhosted.org/packages/52/4f/6daf66394152756664257180439d37047aa9a1cfaa5e4f5ed35e93d1dc06/setproctitle-1.3.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a7d159e7345f343b44330cbba9194169b8590cb13dae940da47aa36a72aa9929", size = 35982, upload-time = "2025-09-05T12:49:56.295Z" }, + { url = "https://files.pythonhosted.org/packages/1b/62/f2c0595403cf915db031f346b0e3b2c0096050e90e0be658a64f44f4278a/setproctitle-1.3.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0b5074649797fd07c72ca1f6bff0406f4a42e1194faac03ecaab765ce605866f", size = 33150, upload-time = "2025-09-05T12:49:58.025Z" }, + { url = "https://files.pythonhosted.org/packages/a0/29/10dd41cde849fb2f9b626c846b7ea30c99c81a18a5037a45cc4ba33c19a7/setproctitle-1.3.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:61e96febced3f61b766115381d97a21a6265a0f29188a791f6df7ed777aef698", size = 34463, upload-time = "2025-09-05T12:49:59.424Z" }, + { url = "https://files.pythonhosted.org/packages/71/3c/cedd8eccfaf15fb73a2c20525b68c9477518917c9437737fa0fda91e378f/setproctitle-1.3.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:047138279f9463f06b858e579cc79580fbf7a04554d24e6bddf8fe5dddbe3d4c", size = 32848, upload-time = "2025-09-05T12:50:01.107Z" }, + { url = "https://files.pythonhosted.org/packages/d1/3e/0a0e27d1c9926fecccfd1f91796c244416c70bf6bca448d988638faea81d/setproctitle-1.3.7-cp313-cp313-win32.whl", hash = "sha256:7f47accafac7fe6535ba8ba9efd59df9d84a6214565108d0ebb1199119c9cbbd", size = 12544, upload-time = "2025-09-05T12:50:15.81Z" }, + { url = "https://files.pythonhosted.org/packages/36/1b/6bf4cb7acbbd5c846ede1c3f4d6b4ee52744d402e43546826da065ff2ab7/setproctitle-1.3.7-cp313-cp313-win_amd64.whl", hash = "sha256:fe5ca35aeec6dc50cabab9bf2d12fbc9067eede7ff4fe92b8f5b99d92e21263f", size = 13235, upload-time = "2025-09-05T12:50:16.89Z" }, + { url = "https://files.pythonhosted.org/packages/e6/a4/d588d3497d4714750e3eaf269e9e8985449203d82b16b933c39bd3fc52a1/setproctitle-1.3.7-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:10e92915c4b3086b1586933a36faf4f92f903c5554f3c34102d18c7d3f5378e9", size = 18058, upload-time = "2025-09-05T12:50:02.501Z" }, + { url = "https://files.pythonhosted.org/packages/05/77/7637f7682322a7244e07c373881c7e982567e2cb1dd2f31bd31481e45500/setproctitle-1.3.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:de879e9c2eab637f34b1a14c4da1e030c12658cdc69ee1b3e5be81b380163ce5", size = 13072, upload-time = "2025-09-05T12:50:03.601Z" }, + { url = "https://files.pythonhosted.org/packages/52/09/f366eca0973cfbac1470068d1313fa3fe3de4a594683385204ec7f1c4101/setproctitle-1.3.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c18246d88e227a5b16248687514f95642505000442165f4b7db354d39d0e4c29", size = 34490, upload-time = "2025-09-05T12:50:04.948Z" }, + { url = "https://files.pythonhosted.org/packages/71/36/611fc2ed149fdea17c3677e1d0df30d8186eef9562acc248682b91312706/setproctitle-1.3.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7081f193dab22df2c36f9fc6d113f3793f83c27891af8fe30c64d89d9a37e152", size = 35267, upload-time = "2025-09-05T12:50:06.015Z" }, + { url = "https://files.pythonhosted.org/packages/88/a4/64e77d0671446bd5a5554387b69e1efd915274686844bea733714c828813/setproctitle-1.3.7-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9cc9b901ce129350637426a89cfd650066a4adc6899e47822e2478a74023ff7c", size = 37376, upload-time = "2025-09-05T12:50:07.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/bc/ad9c664fe524fb4a4b2d3663661a5c63453ce851736171e454fa2cdec35c/setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:80e177eff2d1ec172188d0d7fd9694f8e43d3aab76a6f5f929bee7bf7894e98b", size = 33963, upload-time = "2025-09-05T12:50:09.056Z" }, + { url = "https://files.pythonhosted.org/packages/ab/01/a36de7caf2d90c4c28678da1466b47495cbbad43badb4e982d8db8167ed4/setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:23e520776c445478a67ee71b2a3c1ffdafbe1f9f677239e03d7e2cc635954e18", size = 35550, upload-time = "2025-09-05T12:50:10.791Z" }, + { url = "https://files.pythonhosted.org/packages/dd/68/17e8aea0ed5ebc17fbf03ed2562bfab277c280e3625850c38d92a7b5fcd9/setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5fa1953126a3b9bd47049d58c51b9dac72e78ed120459bd3aceb1bacee72357c", size = 33727, upload-time = "2025-09-05T12:50:12.032Z" }, + { url = "https://files.pythonhosted.org/packages/b2/33/90a3bf43fe3a2242b4618aa799c672270250b5780667898f30663fd94993/setproctitle-1.3.7-cp313-cp313t-win32.whl", hash = "sha256:4a5e212bf438a4dbeece763f4962ad472c6008ff6702e230b4f16a037e2f6f29", size = 12549, upload-time = "2025-09-05T12:50:13.074Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0e/50d1f07f3032e1f23d814ad6462bc0a138f369967c72494286b8a5228e40/setproctitle-1.3.7-cp313-cp313t-win_amd64.whl", hash = "sha256:cf2727b733e90b4f874bac53e3092aa0413fe1ea6d4f153f01207e6ce65034d9", size = 13243, upload-time = "2025-09-05T12:50:14.146Z" }, + { url = "https://files.pythonhosted.org/packages/89/c7/43ac3a98414f91d1b86a276bc2f799ad0b4b010e08497a95750d5bc42803/setproctitle-1.3.7-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:80c36c6a87ff72eabf621d0c79b66f3bdd0ecc79e873c1e9f0651ee8bf215c63", size = 18052, upload-time = "2025-09-05T12:50:17.928Z" }, + { url = "https://files.pythonhosted.org/packages/cd/2c/dc258600a25e1a1f04948073826bebc55e18dbd99dc65a576277a82146fa/setproctitle-1.3.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b53602371a52b91c80aaf578b5ada29d311d12b8a69c0c17fbc35b76a1fd4f2e", size = 13071, upload-time = "2025-09-05T12:50:19.061Z" }, + { url = "https://files.pythonhosted.org/packages/ab/26/8e3bb082992f19823d831f3d62a89409deb6092e72fc6940962983ffc94f/setproctitle-1.3.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fcb966a6c57cf07cc9448321a08f3be6b11b7635be502669bc1d8745115d7e7f", size = 33180, upload-time = "2025-09-05T12:50:20.395Z" }, + { url = "https://files.pythonhosted.org/packages/f1/af/ae692a20276d1159dd0cf77b0bcf92cbb954b965655eb4a69672099bb214/setproctitle-1.3.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46178672599b940368d769474fe13ecef1b587d58bb438ea72b9987f74c56ea5", size = 34043, upload-time = "2025-09-05T12:50:22.454Z" }, + { url = "https://files.pythonhosted.org/packages/34/b2/6a092076324dd4dac1a6d38482bedebbff5cf34ef29f58585ec76e47bc9d/setproctitle-1.3.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7f9e9e3ff135cbcc3edd2f4cf29b139f4aca040d931573102742db70ff428c17", size = 35892, upload-time = "2025-09-05T12:50:23.937Z" }, + { url = "https://files.pythonhosted.org/packages/1c/1a/8836b9f28cee32859ac36c3df85aa03e1ff4598d23ea17ca2e96b5845a8f/setproctitle-1.3.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14c7eba8d90c93b0e79c01f0bd92a37b61983c27d6d7d5a3b5defd599113d60e", size = 32898, upload-time = "2025-09-05T12:50:25.617Z" }, + { url = "https://files.pythonhosted.org/packages/ef/22/8fabdc24baf42defb599714799d8445fe3ae987ec425a26ec8e80ea38f8e/setproctitle-1.3.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9e64e98077fb30b6cf98073d6c439cd91deb8ebbf8fc62d9dbf52bd38b0c6ac0", size = 34308, upload-time = "2025-09-05T12:50:26.827Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/b9bee9de6c8cdcb3b3a6cb0b3e773afdb86bbbc1665a3bfa424a4294fda2/setproctitle-1.3.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b91387cc0f02a00ac95dcd93f066242d3cca10ff9e6153de7ee07069c6f0f7c8", size = 32536, upload-time = "2025-09-05T12:50:28.5Z" }, + { url = "https://files.pythonhosted.org/packages/37/0c/75e5f2685a5e3eda0b39a8b158d6d8895d6daf3ba86dec9e3ba021510272/setproctitle-1.3.7-cp314-cp314-win32.whl", hash = "sha256:52b054a61c99d1b72fba58b7f5486e04b20fefc6961cd76722b424c187f362ed", size = 12731, upload-time = "2025-09-05T12:50:43.955Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/acddbce90d1361e1786e1fb421bc25baeb0c22ef244ee5d0176511769ec8/setproctitle-1.3.7-cp314-cp314-win_amd64.whl", hash = "sha256:5818e4080ac04da1851b3ec71e8a0f64e3748bf9849045180566d8b736702416", size = 13464, upload-time = "2025-09-05T12:50:45.057Z" }, + { url = "https://files.pythonhosted.org/packages/01/6d/20886c8ff2e6d85e3cabadab6aab9bb90acaf1a5cfcb04d633f8d61b2626/setproctitle-1.3.7-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6fc87caf9e323ac426910306c3e5d3205cd9f8dcac06d233fcafe9337f0928a3", size = 18062, upload-time = "2025-09-05T12:50:29.78Z" }, + { url = "https://files.pythonhosted.org/packages/9a/60/26dfc5f198715f1343b95c2f7a1c16ae9ffa45bd89ffd45a60ed258d24ea/setproctitle-1.3.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6134c63853d87a4897ba7d5cc0e16abfa687f6c66fc09f262bb70d67718f2309", size = 13075, upload-time = "2025-09-05T12:50:31.604Z" }, + { url = "https://files.pythonhosted.org/packages/21/9c/980b01f50d51345dd513047e3ba9e96468134b9181319093e61db1c47188/setproctitle-1.3.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1403d2abfd32790b6369916e2313dffbe87d6b11dca5bbd898981bcde48e7a2b", size = 34744, upload-time = "2025-09-05T12:50:32.777Z" }, + { url = "https://files.pythonhosted.org/packages/86/b4/82cd0c86e6d1c4538e1a7eb908c7517721513b801dff4ba3f98ef816a240/setproctitle-1.3.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7c5bfe4228ea22373e3025965d1a4116097e555ee3436044f5c954a5e63ac45", size = 35589, upload-time = "2025-09-05T12:50:34.13Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4f/9f6b2a7417fd45673037554021c888b31247f7594ff4bd2239918c5cd6d0/setproctitle-1.3.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:585edf25e54e21a94ccb0fe81ad32b9196b69ebc4fc25f81da81fb8a50cca9e4", size = 37698, upload-time = "2025-09-05T12:50:35.524Z" }, + { url = "https://files.pythonhosted.org/packages/20/92/927b7d4744aac214d149c892cb5fa6dc6f49cfa040cb2b0a844acd63dcaf/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:96c38cdeef9036eb2724c2210e8d0b93224e709af68c435d46a4733a3675fee1", size = 34201, upload-time = "2025-09-05T12:50:36.697Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0c/fd4901db5ba4b9d9013e62f61d9c18d52290497f956745cd3e91b0d80f90/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:45e3ef48350abb49cf937d0a8ba15e42cee1e5ae13ca41a77c66d1abc27a5070", size = 35801, upload-time = "2025-09-05T12:50:38.314Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e3/54b496ac724e60e61cc3447f02690105901ca6d90da0377dffe49ff99fc7/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1fae595d032b30dab4d659bece20debd202229fce12b55abab978b7f30783d73", size = 33958, upload-time = "2025-09-05T12:50:39.841Z" }, + { url = "https://files.pythonhosted.org/packages/ea/a8/c84bb045ebf8c6fdc7f7532319e86f8380d14bbd3084e6348df56bdfe6fd/setproctitle-1.3.7-cp314-cp314t-win32.whl", hash = "sha256:02432f26f5d1329ab22279ff863c83589894977063f59e6c4b4845804a08f8c2", size = 12745, upload-time = "2025-09-05T12:50:41.377Z" }, + { url = "https://files.pythonhosted.org/packages/08/b6/3a5a4f9952972791a9114ac01dfc123f0df79903577a3e0a7a404a695586/setproctitle-1.3.7-cp314-cp314t-win_amd64.whl", hash = "sha256:cbc388e3d86da1f766d8fc2e12682e446064c01cea9f88a88647cfe7c011de6a", size = 13469, upload-time = "2025-09-05T12:50:42.67Z" }, +] + +[[package]] +name = "setuptools" +version = "80.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70", size = 1200343, upload-time = "2026-01-25T22:38:17.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173", size = 1064234, upload-time = "2026-01-25T22:38:15.216Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/2b/58abc2d1fd397e7dde08e947e05c884d8ef2f78d5e2588c17a12d42d6994/sse_starlette-3.4.4.tar.gz", hash = "sha256:07e0fa0460138baf25cdd5fb28683472c3995dc1642225191b3832d62526bcb0", size = 31819, upload-time = "2026-05-12T17:37:17.019Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/67/805710444ea8cc75fbf70b920ed431a560c4bf9c57f7d5a3117213189399/sse_starlette-3.4.4-py3-none-any.whl", hash = "sha256:3f4dd50d8aed2771a091f3a83000323fc3844541c16b4fe585ae2420cc6df973", size = 16514, upload-time = "2026-05-12T17:37:15.601Z" }, +] + +[[package]] +name = "starlette" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/44/ec35f1b6e83094b997da438a02c8c9b0ade2b1e84cfc48bd4656780760a6/starlette-1.2.1.tar.gz", hash = "sha256:9b9b5ebb992e67d6093741e63c2f59e4f6fff986f81163c087867bd7b924b3f6", size = 2701854, upload-time = "2026-05-31T01:07:51.847Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/54/196d0c1db10af76baa4f64894448505d60d3cdf70ef92cbb35f46a4e4c71/starlette-1.2.1-py3-none-any.whl", hash = "sha256:4de0082d08c8f6764a85a54cf1120d6939507a19905c7768acad2a9f875d2b89", size = 73350, upload-time = "2026-05-31T01:07:50.09Z" }, +] + +[[package]] +name = "supervisor" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/b5/37e7a3706de436a8a2d75334711dad1afb4ddffab09f25e31d89e467542f/supervisor-4.3.0.tar.gz", hash = "sha256:4a2bf149adf42997e1bb44b70c43b613275ec9852c3edacca86a9166b27e945e", size = 468912, upload-time = "2025-08-23T18:25:02.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/65/5e726c372da8a5e35022a94388b12252710aad0c2351699c3d76ae8dba78/supervisor-4.3.0-py2.py3-none-any.whl", hash = "sha256:0bcb763fddafba410f35cbde226aa7f8514b9fb82eb05a0c85f6588d1c13f8db", size = 320736, upload-time = "2025-08-23T18:25:00.767Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tabulate" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" }, +] + +[[package]] +name = "tiktoken" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/e5/5f3cb2159769d0f4324c0e9e87f9de3c4b1cd45848a96b2eb3566ad5ca77/tiktoken-0.13.0.tar.gz", hash = "sha256:c9435714c3a84c2319499de9a300c0e604449dd0799ff246458b3bb6a7f433c1", size = 38986, upload-time = "2026-05-15T04:51:27.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/8e/144bde4e01df66b34bb865557c7cd754ed08b036217ebd79c9db5e9048a9/tiktoken-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:32ac870a806cfb260a02d0cb70426aef02e038297f8ad50df5040bb5af360791", size = 1034888, upload-time = "2026-05-15T04:50:31.579Z" }, + { url = "https://files.pythonhosted.org/packages/36/18/d4ac9d20956cdebca04841316660ed584c2fecdc2b81722a28bc7ad3b1e4/tiktoken-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d9980f11429ed2d737c463bb1fb78cf330caa026adf002f714aced7849a687b", size = 982970, upload-time = "2026-05-15T04:50:32.961Z" }, + { url = "https://files.pythonhosted.org/packages/74/ed/6bb8d05b9f731f749fee5c6f5ca63e981143c826a5985877330507bd13b7/tiktoken-0.13.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3f277ebea5edd7b8bf03c6f9431e1d67d517530115572b2dc1d465326e8f88c7", size = 1115741, upload-time = "2026-05-15T04:50:34.475Z" }, + { url = "https://files.pythonhosted.org/packages/34/de/2ca96b07a82d972b74fe4b46de055b79c904e45c7eab699354a0bfa697dc/tiktoken-0.13.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a116178fa7e1b4065bff05214360373a65cac22f965be7b3f73d00a0dbfe7649", size = 1136523, upload-time = "2026-05-15T04:50:35.782Z" }, + { url = "https://files.pythonhosted.org/packages/ee/dc/9dafec002c2d4424378563cf4cf5c7fb93631d2a55013c8b87554ee4012c/tiktoken-0.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c397ddda233208345b01bd30f2fca79ff730e55731d0108a603f9bc57f6af3b", size = 1181954, upload-time = "2026-05-15T04:50:36.99Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d0/1f8578c45b2f24759b46f0b50d31878c63c73e6bf0f2227e10ec5c5408dc/tiktoken-0.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:95097e4f89b06403976e498abf61a0ee73a7497e73fb599cb211d8197a054d91", size = 1240069, upload-time = "2026-05-15T04:50:38.221Z" }, + { url = "https://files.pythonhosted.org/packages/aa/90/28d7f154888610aa9237e541986beb62b479df29d193a5a0617dbb1514d0/tiktoken-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8f2d16e7a7c783ad81f36e457d046d1f1c8af70b22aec8a13238efe531977c41", size = 874748, upload-time = "2026-05-15T04:50:39.587Z" }, + { url = "https://files.pythonhosted.org/packages/9c/83/b096c859c2a47c11731bf2f5885f4028b809dfe2396582883eed9cae372f/tiktoken-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5df5d1507bd245f1ccad4a074698240021239e455eb0bb4ced4e3d7181872154", size = 1034228, upload-time = "2026-05-15T04:50:40.988Z" }, + { url = "https://files.pythonhosted.org/packages/53/61/c68e123b6d753e3fc2751e9b18e732c9d8bf1e1926762e736eee935d931c/tiktoken-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fe806a50664e83a6ffd56cbd1e4f5dcc6cd32a3e7538f70dc38b1a271384545", size = 982978, upload-time = "2026-05-15T04:50:42.195Z" }, + { url = "https://files.pythonhosted.org/packages/ef/8b/96cc178cc584e65d363134500f297790b06cd48cdeb1e8fcf7bbe60f4715/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:125bc05005e747f993a83dc67934249932d6e4209854452cd4c0b1d53fba3ba2", size = 1116355, upload-time = "2026-05-15T04:50:43.564Z" }, + { url = "https://files.pythonhosted.org/packages/86/f5/bab735d2c72ea55404b295d02d092644eb5f7cc6205e34d35eb9abfb9ab2/tiktoken-0.13.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5e6358911cab4adee6712da27d65573496a4f68cf8a2b5fca6a4ad10fc5748cf", size = 1135772, upload-time = "2026-05-15T04:50:44.782Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b9/6de04ebdf904edfaad87788011b3735087a0c9ea671b9027e1e4e965e8c8/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:975cbd78d085d75d26b59660e262736dcaed1e35f8f142cd6291025c01d25486", size = 1182415, upload-time = "2026-05-15T04:50:46.422Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9c/470a05f3b1caf038f44880e334d47ab674e0c80d514c66b375d14d5afa10/tiktoken-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ab9bc99fa020a4c283424590ecd7f3afd70c1c281cb3fa3192a6c3af9f9615", size = 1239879, upload-time = "2026-05-15T04:50:48.052Z" }, + { url = "https://files.pythonhosted.org/packages/42/a6/c1936d16055436cb32e6c6128d68629622e00f4768562f55653752d34768/tiktoken-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:6b1615f0ff71953d19729ceb18865429c185b0a23c5353f1bbca34a394bf60f7", size = 874829, upload-time = "2026-05-15T04:50:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/d6/07/acb5992c3772b5a36284f742cfb7a5895aa4471d1848ac31464ad50d7fdf/tiktoken-0.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6eb4a5bfbc6426938026b1a334e898ac53541360d62d8c689870160cc80abd67", size = 1033600, upload-time = "2026-05-15T04:50:50.4Z" }, + { url = "https://files.pythonhosted.org/packages/14/e9/742e9aec30f59b9f161f7ff7cd072e02ea836c9e1c0854a8076dfcd40d5c/tiktoken-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:43cee3e5400573b2046fbf092cc7a5bc30164f9e4c95ce20714da929df48737a", size = 982516, upload-time = "2026-05-15T04:50:52.03Z" }, + { url = "https://files.pythonhosted.org/packages/72/74/ca1541b053e7648254d2e4b42a253e1bb4359f2c91a0a8d49228c794e1a0/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7de52e3f566d19b3b11bd37eea552c6c305ad74081f736882bd44d148ed4c48d", size = 1115518, upload-time = "2026-05-15T04:50:53.543Z" }, + { url = "https://files.pythonhosted.org/packages/46/e3/93825eaf5a4a504795b787e5d5dea07fbeb3dabf97aa7b450be8bde59c89/tiktoken-0.13.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:51384448aa508e4df84c0f7c1dc3211c7f7b8096325660ee5fc82f3e11b381ce", size = 1136867, upload-time = "2026-05-15T04:50:55.191Z" }, + { url = "https://files.pythonhosted.org/packages/8c/46/002b68de6827091d5ae90b048f326e8aad8d953520950e5ce1508879414f/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e28157350f7ebf35008dd8e9e0fdb621f976e4230c881099c85e8cf07eaa50e2", size = 1181826, upload-time = "2026-05-15T04:50:56.296Z" }, + { url = "https://files.pythonhosted.org/packages/db/c6/d393e3185a276505182f7abd93fe714f3c444a2be9180798fa052347504e/tiktoken-0.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:165cf1820ea4a354985c2490a5205d4cc74661c934aca79dd0368232fff94e0f", size = 1239489, upload-time = "2026-05-15T04:50:57.918Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4d/bc07d1f1635d4897a202acc0ae11c2886eaa7325c359ba4741b47bf8e225/tiktoken-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6c43a675ca14f6f2749ba7f12075d37456015a24b859f2517b9beb4ef30807ec", size = 873820, upload-time = "2026-05-15T04:50:59.528Z" }, + { url = "https://files.pythonhosted.org/packages/8c/93/0dd6adca026a616c3a92974566b43381eea4b475ce1f36c062b8271a9ac5/tiktoken-0.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaaaef47c2406277181d2086484c317bf7fc433e2d5d03ff94f56b0dcec87471", size = 1034977, upload-time = "2026-05-15T04:51:00.957Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5ec6e6bc5b30bed6d93f7f2162d8f6b32437b3ba27cb527cfe004f6109c9/tiktoken-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ca8b310bd93b3772cb1b7922d915446864860f562bdfe4825c63a0aed3fb28cd", size = 983635, upload-time = "2026-05-15T04:51:02.629Z" }, + { url = "https://files.pythonhosted.org/packages/94/b0/c8ae9aff00d625c50659b4513e707a0462c4bf5d4d6cc1b802103225c02e/tiktoken-0.13.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:32e0c12305105002c047b3bb1070b0dd9a73b0cb3b2856a8972b810e7a4f5881", size = 1116036, upload-time = "2026-05-15T04:51:04.082Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ac/6a5dddd1d0a6018ecb389bd0353e6b4a515eb4d2286611bd0ace1937b9e1/tiktoken-0.13.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:5ba5fd62507a932d1241346179e3b39bc7bf7408f03c272652d93b3bedf5db24", size = 1135544, upload-time = "2026-05-15T04:51:05.229Z" }, + { url = "https://files.pythonhosted.org/packages/f4/b8/585032b4384b2f7dcdaddcb52865c83a701a420d09e3c2b4a2be1c450c57/tiktoken-0.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d108bc2d470fc53c8ecd24f2c0fd2b5f98c33e87cdb6aa2e9b8c5dced703d273", size = 1182217, upload-time = "2026-05-15T04:51:06.517Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b6/993ff1ded3958215fd341a847b8e5ffeb5de473f435296870d314fc91ac4/tiktoken-0.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cb99cb5127449f58d0a2d5f5ccfb390d8dbdfd919c221246caaee29d8725ed51", size = 1239404, upload-time = "2026-05-15T04:51:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3d/fef7e06e3b33e7538db0ced734cf9fe23b6832d2ac4990c119c377aec55e/tiktoken-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:115c4f26ffa11caac8b54eea35c2ad38c612c20a48d35dd15d70a02ac6f51f58", size = 918686, upload-time = "2026-05-15T04:51:08.925Z" }, + { url = "https://files.pythonhosted.org/packages/c1/82/a7fc44582bc32ab00de988a2299bf77c077f59068b233109e34b7d6ca7e6/tiktoken-0.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:472527e9132952f2fbf77cd290658bacf003d4d5a3fabc18e5fbd407cbae4d9b", size = 1034454, upload-time = "2026-05-15T04:51:10.035Z" }, + { url = "https://files.pythonhosted.org/packages/37/d0/24d8a890c14f432a05cea669c17bebeaa99f96a7c79523b590f564246411/tiktoken-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e2f67d27c9626cdd25fe33d9313c5cdb3d8d82da646b68d6eb8e7e9c20e6448", size = 982976, upload-time = "2026-05-15T04:51:11.23Z" }, + { url = "https://files.pythonhosted.org/packages/49/b7/2ab43f62788a9266187a9bfc1d3af99ad83e5eaa25fbef168a69cd5ad14f/tiktoken-0.13.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2b920b35805cd64585a37c3dc7ce65fba4d2d36016be01e1d7942482ca29093a", size = 1115526, upload-time = "2026-05-15T04:51:12.608Z" }, + { url = "https://files.pythonhosted.org/packages/64/39/1494321ed323ce7a14d88e3cd6cb9058625977df1c6961ddc492bd10a9f3/tiktoken-0.13.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:493af3aa28a4aaf2e3d2600a2ee717252c9bf5ab38fff94eb5a02db5ab77e5ad", size = 1136466, upload-time = "2026-05-15T04:51:13.926Z" }, + { url = "https://files.pythonhosted.org/packages/96/d9/dfd086aa2d918c563a140720e0ce296cada1634efd2783d5cf51e05f984e/tiktoken-0.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6644c9c2b5cf3916f5a3641d7d12fdb3f006a7b3d9ff6acdaec44e29ab1ff91e", size = 1181863, upload-time = "2026-05-15T04:51:15.025Z" }, + { url = "https://files.pythonhosted.org/packages/2f/68/a18b4f307086954fdae32714cb4f85562e34f9d34ab206e61f1816aa6018/tiktoken-0.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5cb65b60b9408563676d874a3a4ee573370066f0dc4e29d84e82e989c6517424", size = 1239218, upload-time = "2026-05-15T04:51:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/16/5b/f2aa703a4fc5d2dff73460a7d46cc2f3f44aa0f3dd8eeb20d2a0ecf68862/tiktoken-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:85b78cc3a2c3d48723ca751fa981f1fedccd54194ca0471b957364353a898b07", size = 918110, upload-time = "2026-05-15T04:51:17.237Z" }, +] + +[[package]] +name = "tilelang" +version = "0.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "apache-tvm-ffi" }, + { name = "cloudpickle" }, + { name = "ml-dtypes" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "psutil" }, + { name = "setuptools", marker = "sys_platform == 'darwin'" }, + { name = "torch" }, + { name = "torch-c-dlpack-ext", marker = "python_full_version < '3.14'" }, + { name = "tqdm" }, + { name = "typing-extensions" }, + { name = "z3-solver" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/70/5051f65821baa30a3d61fc48f8ba10c776490315e8c90f82559b92089756/tilelang-0.1.9.tar.gz", hash = "sha256:287f727c913bb648fcf6c1968809ba3390e55eeed257a5c6bb9a80bc05966af4", size = 93395292, upload-time = "2026-04-22T09:19:11.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/db/4dd76da8c8585c605639a21bc098d504e317fe324a72f01ce3c7370250b4/tilelang-0.1.9-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:00ed594fdeb229c5505b9ffa895c3c5daeb28641c78f783fa1f724cf1e08cecd", size = 36599020, upload-time = "2026-04-22T09:14:39.366Z" }, + { url = "https://files.pythonhosted.org/packages/f7/8a/1cbeee79d62abaa02441c2d00621554e41aa62dbf3b94a4feb3867184b01/tilelang-0.1.9-cp38-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bbccfe9035aed775ffafb6dc25a5994504b24e2c5d95d0f39643edfafa7bf12", size = 45419374, upload-time = "2026-04-22T09:15:56.014Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a7/f4bfb86f87e107703146e703204cec2c0eae2492b633e0052b0ace3febb6/tilelang-0.1.9-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:77ab0ee2f40f66ea015b6b21426d482751e28cbc635ef9d1198cbd6502454a7c", size = 42110365, upload-time = "2026-04-22T09:17:18.292Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, +] + +[[package]] +name = "tokenspeed-mla" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "apache-tvm-ffi" }, + { name = "nvidia-cutlass-dsl" }, + { name = "tokenspeed-triton" }, + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/20/4110d624d81d63f0bee2f19dba7ea0e1d8a31ea50147e6c1db82223c88a4/tokenspeed_mla-0.1.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:592590f36d85e624ecdc5e357ff35e29e761e6d879900dce8b67a6785c8ce75c", size = 743769, upload-time = "2026-05-13T03:30:54.486Z" }, + { url = "https://files.pythonhosted.org/packages/84/01/4bf8b74ead3e8e7c1c809435396254c067a33fde48acc20f602aae622d97/tokenspeed_mla-0.1.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:c9466a351fe039792e56cf49f3e79744c1dc28c7af10306a02e62b8e92fa5985", size = 748681, upload-time = "2026-05-13T03:30:56.718Z" }, +] + +[[package]] +name = "tokenspeed-triton" +version = "3.7.10.post20260531" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/58/fdb5fb70d99c1f18f01c2198420fa2a0f7e5301bd7dd5b5f34b22a3cb87b/tokenspeed_triton-3.7.10.post20260531-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16cd0a3fc1cffeb458a7e03e8688714f49fdf0b5a108bfca999f46597c3faabb", size = 81636010, upload-time = "2026-05-31T01:29:17.699Z" }, + { url = "https://files.pythonhosted.org/packages/d7/49/7bae94729bfd7a3f331795251302f0b0c8e54a7ec25b3af5d5bfe133367c/tokenspeed_triton-3.7.10.post20260531-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b90ac41e7f15933797545ff1a9e803a9d8beb4ca9ba70f6d41a9e0fc26484f5c", size = 85888791, upload-time = "2026-05-31T01:29:25.584Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, +] + +[[package]] +name = "torch" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" }, + { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115, upload-time = "2026-03-23T18:11:06.944Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279, upload-time = "2026-03-23T18:10:31.481Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047, upload-time = "2026-03-23T18:10:55.931Z" }, + { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801, upload-time = "2026-03-23T18:10:18.649Z" }, + { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382, upload-time = "2026-03-23T18:08:30.835Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509, upload-time = "2026-03-23T18:08:47.213Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842, upload-time = "2026-03-23T18:09:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574, upload-time = "2026-03-23T18:10:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324, upload-time = "2026-03-23T18:09:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026, upload-time = "2026-03-23T18:09:20.842Z" }, + { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702, upload-time = "2026-03-23T18:09:47.304Z" }, + { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442, upload-time = "2026-03-23T18:09:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385, upload-time = "2026-03-23T18:07:33.77Z" }, + { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756, upload-time = "2026-03-23T18:07:50.02Z" }, + { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300, upload-time = "2026-03-23T18:09:05.617Z" }, + { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460, upload-time = "2026-03-23T18:09:00.818Z" }, + { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835, upload-time = "2026-03-23T18:07:18.974Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405, upload-time = "2026-03-23T18:08:07.014Z" }, + { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991, upload-time = "2026-03-23T18:08:19.216Z" }, +] + +[[package]] +name = "torch-c-dlpack-ext" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/de/921b6491efce5c389a5ef9bbed3d2d6660005840dae488124173180859ab/torch_c_dlpack_ext-0.1.5.tar.gz", hash = "sha256:d06f0357d575d22a168cc77acb9020fc4bae30968ceb6718a055dcbe92bacabe", size = 12913, upload-time = "2026-01-12T11:25:08.484Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/67/10d236698525d7b7db4d74ec0a4b01f5b2db33968995fdd9ac6b4635e327/torch_c_dlpack_ext-0.1.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:c0f2bd51fcd99c0e5b50314e1985f2728c4941bfa821f065e6c30951d1f995ca", size = 5291237, upload-time = "2026-01-12T11:24:44.011Z" }, + { url = "https://files.pythonhosted.org/packages/87/06/8d760997307a5c3be4384424667bf31aae0a42060838c532c7d846516175/torch_c_dlpack_ext-0.1.5-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3562ee411258676f9c38b8ad39306d1c8d027b6a86f6a87c920d2d009a9d1510", size = 443069, upload-time = "2026-01-12T11:24:45.451Z" }, + { url = "https://files.pythonhosted.org/packages/e2/79/a914539b4785f3e44f891aa012a886edb8bc10fe081c440981c57543ce21/torch_c_dlpack_ext-0.1.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e6f9da4bb9af70e27facc777458be62e10dbbbddda7672d16138db0553c5a524", size = 897846, upload-time = "2026-01-12T11:24:48.168Z" }, + { url = "https://files.pythonhosted.org/packages/3a/e6/7d7a97a3953208d6d6ce749180c34d1dab48464ded9a76cecabe9d021ce6/torch_c_dlpack_ext-0.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:670fbbab70123cc228bed41693a3720757af57a0ad22669063c9db25321e8f55", size = 1482855, upload-time = "2026-01-12T11:24:49.581Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c6/65346a201d921b616731311fc9941f15137672b444cebdad702cb52ccee0/torch_c_dlpack_ext-0.1.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:74acea2ed395cadda63342845b9e9ee7cd4537846223dacfb4431b4610109265", size = 1993243, upload-time = "2026-01-12T11:24:51.079Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ec/faf10be09a5812b1c5ec9922b53fb5def5fc4080b81a653b9347bb169ebb/torch_c_dlpack_ext-0.1.5-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49f1e99d13c64e22dac0a34a1560e9e5a398a49a9fa81df83053e04fde6ec5bd", size = 443798, upload-time = "2026-01-12T11:24:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/2d/68/f434b48700f3e04f33882f54d8d3910327b935f55e14ec49da7d607bf470/torch_c_dlpack_ext-0.1.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:debe62e5ef93e631065d6b9f6e60d3d39bae6b89fa1b25d9523f40b3efbf8aba", size = 755004, upload-time = "2026-01-12T11:24:54.004Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/cc64e563f05ea99bd79bdb43f71f0f46452d3acd734da4843ede5fc73a35/torch_c_dlpack_ext-0.1.5-cp313-cp313-win_amd64.whl", hash = "sha256:30e3eab616dbc81dfdb7492aca557be551a9163ba9b585f97394a42b336b113a", size = 999126, upload-time = "2026-01-12T11:24:55.44Z" }, + { url = "https://files.pythonhosted.org/packages/96/5e/449324ca8e81573e650b6851fc31c1038f750d1de85d0b185d788e1c7a3a/torch_c_dlpack_ext-0.1.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:cac94a4905d391889e679a8da31e46dc325af5d55d13b7c70c0ce3d71d1ced6d", size = 1982154, upload-time = "2026-01-12T11:24:58.038Z" }, + { url = "https://files.pythonhosted.org/packages/20/62/11c05b99f69aa5152bca0313e0dfa6d125a020cf890dc888ef009aa7891c/torch_c_dlpack_ext-0.1.5-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a58fdf45fb0bda7bc459632cec891570f31c11636d5851c825cf308ec8b73c2", size = 163825, upload-time = "2026-01-12T11:24:59.474Z" }, + { url = "https://files.pythonhosted.org/packages/15/b5/be613cd8e71c9982bd07af530f86c5a7f30df7831d14cec5414857af7149/torch_c_dlpack_ext-0.1.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b985a324c68241cf83a9474b28015524b66775b12a91930dd4c0760aa628d01", size = 171740, upload-time = "2026-01-12T11:25:00.776Z" }, + { url = "https://files.pythonhosted.org/packages/5c/11/52e291f1659e2ec70a09f5ca4ad27e015eb4f0a1371ae68d23a9fbd1c704/torch_c_dlpack_ext-0.1.5-cp314-cp314-win_amd64.whl", hash = "sha256:d794e19fa3f330ab7a29987c07e031fc08e4953aec516d35701d0827863e356b", size = 277086, upload-time = "2026-01-12T11:25:01.901Z" }, +] + +[[package]] +name = "torchaudio" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/b1/77658817acacd01a72b714440c62f419efc4d90170e704e8e7a2c0918988/torchaudio-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1cf1acc883bee9cb906a933572fed6a8a933f86ef34e9ea7d803f72317e8c1b", size = 684226, upload-time = "2026-03-23T18:13:40.023Z" }, + { url = "https://files.pythonhosted.org/packages/78/28/c7adc053039f286c2aca0038b766cbe3294e66fec6b29a820e95128f9ede/torchaudio-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:bc653defca1c16154398517a1adc98d0fb7f1dd08e58ced217558d213c2c6e29", size = 1626670, upload-time = "2026-03-23T18:13:42.162Z" }, + { url = "https://files.pythonhosted.org/packages/88/d8/d6d0f896e064aa67377484efef4911cdcc07bce2929474e1417cc0af18c2/torchaudio-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6503c0bdb29daf2e6281bb70ea2dfe2c3553b782b619eb5d73bdadd8a3f7cecf", size = 1771992, upload-time = "2026-03-23T18:13:33.188Z" }, + { url = "https://files.pythonhosted.org/packages/23/a8/941277ecc39f7a0a169d554302a1f1afd87c1d94a8aec828891916cea59a/torchaudio-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:478110f981e5d40a8d82221732c57a56c85a1d5895fb8fe646e86ee15eded3bd", size = 328663, upload-time = "2026-03-23T18:13:19.218Z" }, + { url = "https://files.pythonhosted.org/packages/fb/9e/f76fcd9877c8c78f258ee34e0fb8291fdb91e6218d582d9ca66b1e4bd4ae/torchaudio-2.11.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e3f9696a9ef1d49acc452159b052370c636406d072e9d8f10895fda87b591ea9", size = 679904, upload-time = "2026-03-23T18:13:28.329Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/249c1498ebdad3e7752866635ec0855fc0dcf898beccda5a9d2b9df8e4d0/torchaudio-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:b034d7672f1c415434f48ef17807f2cce47f29e8795338c751d4e596c9fbe8b5", size = 1618523, upload-time = "2026-03-23T18:13:15.703Z" }, + { url = "https://files.pythonhosted.org/packages/4f/98/be13fe35d9aa5c26381c0e453c828a789d15c007f8f7d08c95341d19974d/torchaudio-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1c1101c1243ef0e4063ec63298977e2d3655c15cf88d9eb0a1bd4fe2db9f47ea", size = 1771992, upload-time = "2026-03-23T18:13:35.343Z" }, + { url = "https://files.pythonhosted.org/packages/e2/8b/2bbb3dca6ff28cba0de250874d5ef4fc2822c47a934b59b3974cff3219ef/torchaudio-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:986f4df5ed17b003dc52489468601720090e65f964f8bebccf90eb45bba75744", size = 328662, upload-time = "2026-03-23T18:13:18.308Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ce/52c652d30af7d6e96c8f1735d26131e94708e3f38d852b8fa97958804dd8/torchaudio-2.11.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:bda09ea630ae7207384fb0f28c35e4f8c0d82dd6eba020b6b335ad0caa9fed49", size = 680814, upload-time = "2026-03-23T18:13:17.08Z" }, + { url = "https://files.pythonhosted.org/packages/06/95/1ad1507482e7263e556709a3f5f87fecd375a0742cdaf238806c8e72eaad/torchaudio-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:9fe3083c62e035646483a14e180d33561bdc2eed436c9ab1259c137fb7120b4a", size = 1618546, upload-time = "2026-03-23T18:13:29.686Z" }, + { url = "https://files.pythonhosted.org/packages/98/4c/480328ba07487eb9890406720304d0d460dd7a6a64098614f5aa53b662ca/torchaudio-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:13cff988697ccbad539987599f9dc672f40c417bed67570b365e4e5002bbd096", size = 1771991, upload-time = "2026-03-23T18:13:30.843Z" }, + { url = "https://files.pythonhosted.org/packages/3e/98/5d4790e2d6548768999acd34999d5aeefce8bcc23a07afaa5f03e723f557/torchaudio-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ed404c4399ad7f172c86a47c1b25293d322d1d58e26b10b0456a86cf67d37d84", size = 328661, upload-time = "2026-03-23T18:13:34.359Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/ffa618b4f0d9732d7df7a2fa2bd48657d896599bc224e5af3c70d46c546b/torchaudio-2.11.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:cc09cd1f6015b8549e7fe255fb1be5346b57e7fee06541d3f3dbb012d8c4715f", size = 679901, upload-time = "2026-03-23T18:13:25.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/54/f414d7b92dd0b3094a2409c95a97bd6c49aa0620da722a0e55462f9bd9cb/torchaudio-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:79fb3cb99169fd41bd9719647261402a164da0d105a4d81f42a3260844ec5e79", size = 1618527, upload-time = "2026-03-23T18:13:26.68Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a8/bf2e1f6ce24c990192400ae49b4acc1a0d0295b6c6a06bceecdc46ce08de/torchaudio-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:00e9f71ab9c656f0abdb40c515bd65d4658ab0ad380dee27a2efd7d51dabd3d6", size = 1771995, upload-time = "2026-03-23T18:13:23.373Z" }, + { url = "https://files.pythonhosted.org/packages/83/6f/b0efb44e0bfe8dd4d78d76ae3be280354e1fb5c8631c782785d74cd8a7b1/torchaudio-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:1424638adb8bb40087bc7b6eb103e8e4fe398210f09076f33b7b5e61501b5d66", size = 328662, upload-time = "2026-03-23T18:13:32.243Z" }, + { url = "https://files.pythonhosted.org/packages/60/84/1c792b0b700eac9a96772cfd9f96c097b17bca3234a2fde3c64b8063660d/torchaudio-2.11.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:da2725e250866da42a12934c9a6552f65a18b7187fd7a6221387f0e605fb3b96", size = 679926, upload-time = "2026-03-23T18:13:24.452Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a0/62a5842062f739239691f2e57523e0570dd06704ad987755f7644a3afa23/torchaudio-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:1be3767064364ae82705bdf2b15c1e8b41fea82c4cd04d47428a8684b634b6ed", size = 1618552, upload-time = "2026-03-23T18:13:21.09Z" }, + { url = "https://files.pythonhosted.org/packages/6d/89/c293d818f9f899db93bf291b42401c05ae29acfb2e53d5341c30ea703e62/torchaudio-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:67f6edac29ed004652c11db5c19d9debb5d835695930574f564efc8bdd061bba", size = 1771986, upload-time = "2026-03-23T18:13:22.153Z" }, + { url = "https://files.pythonhosted.org/packages/93/f7/ee5da8c03f1a3c7662c6c6a119f24a4b3e646da94be56dce3201e3a6ee9b/torchaudio-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:88fb5e29f670a33d9bac6aabb1d2734460cf6e461bde5cdc352826035851b16d", size = 328661, upload-time = "2026-03-23T18:13:20.1Z" }, +] + +[[package]] +name = "torchvision" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/e7/56b47cc3b132aea90ccce22bcb8975dec688b002150012acc842846039d0/torchvision-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c409e1c3fdebec7a3834465086dbda8bf7680eff79abf7fd2f10c6b59520a7a4", size = 1863502, upload-time = "2026-03-23T18:12:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ec/5c31c92c08b65662fe9604a4067ae8232582805949f11ddc042cebe818ed/torchvision-0.26.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:406557718e62fdf10f5706e88d8a5ec000f872da913bf629aab9297622585547", size = 7767944, upload-time = "2026-03-23T18:12:42.805Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d8/cb6ccda1a1f35a6597645818641701207b3e8e13553e75fce5d86bac74b2/torchvision-0.26.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d61a5abb6b42a0c0c311996c2ac4b83a94418a97182c83b055a2a4ae985e05aa", size = 7522205, upload-time = "2026-03-23T18:12:54.654Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a9/c272623a0f735c35f0f6cd6dc74784d4f970e800cf063bb76687895a2ab9/torchvision-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:7993c01648e7c61d191b018e84d38fe0825c8fcb2720cd0f37caf7ba14404aa1", size = 4255155, upload-time = "2026-03-23T18:12:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/0762f77f53605d10c9477be39bb47722cc8e383bbbc2531471ce0e396c07/torchvision-0.26.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5d63dd43162691258b1b3529b9041bac7d54caa37eae0925f997108268cbf7c4", size = 1860809, upload-time = "2026-03-23T18:12:47.629Z" }, + { url = "https://files.pythonhosted.org/packages/e6/81/0b3e58d1478c660a5af4268713486b2df7203f35abd9195fea87348a5178/torchvision-0.26.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a39c7a26538c41fda453f9a9692b5ff9b35a5437db1d94f3027f6f509c160eac", size = 7727494, upload-time = "2026-03-23T18:12:46.062Z" }, + { url = "https://files.pythonhosted.org/packages/b6/dc/d9ab5d29115aa05e12e30f1397a3eeae1d88a511241dc3bce48dc4342675/torchvision-0.26.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b7e6213620bbf97742e5f79832f9e9d769e6cf0f744c5b53dad80b76db633691", size = 7521747, upload-time = "2026-03-23T18:12:36.815Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1b/f1bc86a918c5f6feab1eeff11982e2060f4704332e96185463d27855bdf5/torchvision-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:4280c35ec8cba1fcc8294fb87e136924708726864c379e4c54494797d86bc474", size = 4319880, upload-time = "2026-03-23T18:12:38.168Z" }, + { url = "https://files.pythonhosted.org/packages/66/28/b4ad0a723ed95b003454caffcc41894b34bd8379df340848cae2c33871de/torchvision-0.26.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:358fc4726d0c08615b6d83b3149854f11efb2a564ed1acb6fce882e151412d23", size = 1951973, upload-time = "2026-03-23T18:12:48.781Z" }, + { url = "https://files.pythonhosted.org/packages/71/e2/7a89096e6cf2f3336353b5338ba925e0addf9d8601920340e6bdf47e8eb3/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:3daf9cc149cf3cdcbd4df9c59dae69ffca86c6823250442c3bbfd63fc2e26c61", size = 7728679, upload-time = "2026-03-23T18:12:26.196Z" }, + { url = "https://files.pythonhosted.org/packages/69/1d/4e1eebc17d18ce080a11dcf3df3f8f717f0efdfa00983f06e8ba79259f61/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:82c3965eca27e86a316e31e4c3e5a16d353e0bcbe0ef8efa2e66502c54493c4b", size = 7609138, upload-time = "2026-03-23T18:12:35.327Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a4/f1155e943ae5b32400d7000adc81c79bb0392b16ceb33bcf13e02e48cced/torchvision-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ebc043cc5a4f0bf22e7680806dbba37ffb19e70f6953bbb44ed1a90aeb5c9bea", size = 4248202, upload-time = "2026-03-23T18:12:41.423Z" }, + { url = "https://files.pythonhosted.org/packages/7f/c8/9bffa9c7f7bdf95b2a0a2dc535c290b9f1cc580c3fb3033ab1246ffffdeb/torchvision-0.26.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:eb61804eb9dbe88c5a2a6c4da8dec1d80d2d0a6f18c999c524e32266cb1ebcd3", size = 1860813, upload-time = "2026-03-23T18:12:39.636Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ac/48f28ffd227991f2e14f4392dde7e8dc14352bb9428c1ef4a4bbf5f7ed85/torchvision-0.26.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:9a904f2131cbfadab4df828088a9f66291ad33f49ff853872aed1f86848ef776", size = 7727777, upload-time = "2026-03-23T18:12:22.549Z" }, + { url = "https://files.pythonhosted.org/packages/a4/21/a2266f7f1b0e58e624ff15fd6f01041f59182c49551ece0db9a183071329/torchvision-0.26.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:0f3e572efe62ad645017ea847e0b5e4f2f638d4e39f05bc011d1eb9ac68d4806", size = 7522174, upload-time = "2026-03-23T18:12:29.565Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ba/1666f90bc0bdd77aaa11dcc42bb9f621a9c3668819c32430452e3d404730/torchvision-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:114bec0c0e98aa4ba446f63e2fe7a2cbca37b39ac933987ee4804f65de121800", size = 4348469, upload-time = "2026-03-23T18:12:24.44Z" }, + { url = "https://files.pythonhosted.org/packages/45/8f/1f0402ac55c2ae15651ff831957d083fe70b2d12282e72612a30ba601512/torchvision-0.26.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:b7d3e295624a28b3b1769228ce1345d94cf4d390dd31136766f76f2d20f718da", size = 1860826, upload-time = "2026-03-23T18:12:34.1Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6a/18a582fe3c5ee26f49b5c9fb21ad8016b4d1c06d10178894a58653946fda/torchvision-0.26.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:7058c5878262937e876f20c25867b33724586aa4499e2853b2d52b99a5e51953", size = 7729089, upload-time = "2026-03-23T18:12:31.394Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9b/f7e119b59499edc00c55c03adc9ec3bd96144d9b81c46852c431f9c64a9a/torchvision-0.26.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:8008474855623c6ba52876589dc52df0aa66e518c25eca841445348e5f79844c", size = 7522704, upload-time = "2026-03-23T18:12:20.301Z" }, + { url = "https://files.pythonhosted.org/packages/d0/6a/09f3844c10643f6c0de5d95abc863420cfaf194c88c7dffd0ac523e2015f/torchvision-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e9d0e022c19a78552fb055d0414d47fecb4a649309b9968573daea160ba6869c", size = 4454275, upload-time = "2026-03-23T18:12:27.487Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "trackio" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gradio-client" }, + { name = "huggingface-hub" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "orjson" }, + { name = "pillow" }, + { name = "python-multipart" }, + { name = "starlette" }, + { name = "uvicorn", extra = ["standard"] }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/90/b40e4e0d8850d7cb5630bc3d5f13cd49bd680250931af33e61162701f94c/trackio-0.26.0-py3-none-any.whl", hash = "sha256:2e10e65ead4048b5977fb1b4fba6b1845cb25148697ec1d91b914c7ffa056bd8", size = 1668713, upload-time = "2026-05-23T00:19:33.322Z" }, +] + +[[package]] +name = "transformers" +version = "5.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/58/7f843608f2e8421f86bb97060b54649be6239ec612b82bf9d41e65c26c00/transformers-5.9.0.tar.gz", hash = "sha256:25997cb8fa6053533171634b6162d7df54346530ec2aa9b42bb834e63668c842", size = 8642240, upload-time = "2026-05-20T14:50:49.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/ca/2eaa5359f2ccb8c2e1656bc26305ad0cf438aa392ce4b29ae67a315c186e/transformers-5.9.0-py3-none-any.whl", hash = "sha256:1d19509bcff7028ebc6b277d71caa712e8353778463d38764237d14b42b52788", size = 10787648, upload-time = "2026-05-20T14:50:45.337Z" }, +] + +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243, upload-time = "2026-01-20T16:16:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" }, + { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, + { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" }, + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, + { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" }, + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, +] + +[[package]] +name = "trl" +version = "1.6.0.dev0" +source = { git = "https://github.com/rycerzes/trl?rev=feat%2Fasync-grpo-parity#011fc6e0368f9f99d69da8bc478893d722a76c88" } +dependencies = [ + { name = "accelerate" }, + { name = "datasets" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "transformers" }, +] + +[[package]] +name = "typer" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +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/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]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "uncalled-for" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/82/345cc927f7fbdae6065e7768759932fcc827fc20b29b45dfbafa2f1f7da4/uncalled_for-0.3.2.tar.gz", hash = "sha256:89f5dbcd71e2b8f47c030b1fa302e6cce2ec795d1ac565eeb6525c5fe55cb8a2", size = 50032, upload-time = "2026-05-06T13:38:25.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/25/2c87754f3a9e692315f7b811244090e68f362979fc8886b3fbd2985a1d8c/uncalled_for-0.3.2-py3-none-any.whl", hash = "sha256:0ff60b142c7d1f8070bde9d42afaa70aedc77dcc10998c227687e9c15713418e", size = 11444, upload-time = "2026-05-06T13:38:24.025Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + +[[package]] +name = "vllm" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "anthropic" }, + { name = "apache-tvm-ffi" }, + { name = "blake3" }, + { name = "cachetools" }, + { name = "cbor2" }, + { name = "cloudpickle" }, + { name = "compressed-tensors" }, + { name = "depyf" }, + { name = "diskcache" }, + { name = "einops" }, + { name = "fastapi", extra = ["standard"] }, + { name = "fastsafetensors" }, + { name = "filelock" }, + { name = "flashinfer-cubin" }, + { name = "flashinfer-python" }, + { name = "gguf" }, + { name = "humming-kernels", extra = ["cu13"] }, + { name = "ijson" }, + { name = "lark" }, + { name = "llguidance", marker = "platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'ppc64le' or platform_machine == 'x86_64'" }, + { name = "lm-format-enforcer" }, + { name = "mcp" }, + { name = "mistral-common", extra = ["image"] }, + { name = "model-hosting-container-standards" }, + { name = "msgspec" }, + { name = "ninja" }, + { name = "numba" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "nvidia-cudnn-frontend" }, + { name = "nvidia-cutlass-dsl", extra = ["cu13"] }, + { name = "openai" }, + { name = "openai-harmony" }, + { name = "opencv-python-headless" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp" }, + { name = "opentelemetry-sdk" }, + { name = "opentelemetry-semantic-conventions-ai" }, + { name = "outlines-core" }, + { name = "partial-json-parser" }, + { name = "pillow" }, + { name = "prometheus-client" }, + { name = "prometheus-fastapi-instrumentator" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "py-cpuinfo" }, + { name = "pybase64" }, + { name = "pydantic" }, + { name = "python-json-logger" }, + { name = "pyyaml" }, + { name = "pyzmq" }, + { name = "quack-kernels" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "sentencepiece" }, + { name = "setproctitle" }, + { name = "setuptools" }, + { name = "six" }, + { name = "tiktoken" }, + { name = "tilelang" }, + { name = "tokenizers" }, + { name = "tokenspeed-mla" }, + { name = "torch" }, + { name = "torchaudio" }, + { name = "torchvision" }, + { name = "tqdm" }, + { name = "transformers" }, + { name = "typing-extensions" }, + { name = "watchfiles" }, + { name = "xgrammar", marker = "platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'ppc64le' or platform_machine == 's390x' or platform_machine == 'x86_64'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/bf/46631fd8e2e9d81c5abe2ab923e5367754bc0cad685c4ddac1d5d86d91b5/vllm-0.22.0.tar.gz", hash = "sha256:6d41581a9e5288cd69278518a550c6d7ce510ae27a506556a3427d01284be7fe", size = 36239170, upload-time = "2026-05-29T10:35:39.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e8/05a69dbd7416c5a5ac91f51e626fede9ceeabe9c6fe243fc11e2b3e1ad3e/vllm-0.22.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0fbe1ff32e9ad82c56b002de11b061ca6b5b8a256cd11473946d2222115ed267", size = 252942448, upload-time = "2026-05-29T10:32:10.261Z" }, + { url = "https://files.pythonhosted.org/packages/ca/23/3f7f759763fb9b4cf5787bcb4a43f74904f8e644d53d4fdb4e19654a92fd/vllm-0.22.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c387a977e35795e8f77b009e019e69722963819c26b55e4a679e09d4279ae35d", size = 261034920, upload-time = "2026-05-29T10:33:10.357Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/2f/e42c992d2afda3108ea1c02acecc991b9f31d05c14adc2a7cee9ee211fc4/watchfiles-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26", size = 400115, upload-time = "2026-05-18T04:32:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8f/6af2ea19065c91d8b0ea3516fdfc8c0d349f407e8e9fbf4e5a17360de8ad/watchfiles-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c", size = 393659, upload-time = "2026-05-18T04:30:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/b32a967c56fb3e3e5be3db52c3d3b87fa4513aa367d8ed1ad96d42952e5f/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc", size = 453207, upload-time = "2026-05-18T04:31:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/97557a812180338cb1abd32e1cffcc4588f59b5f23e0cb006b2ba95ba64a/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0", size = 459273, upload-time = "2026-05-18T04:31:50.377Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a8/b4b08dcb7653b8087c6586f7ce649505900e866bbcfe40dc9587af02e686/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c", size = 489927, upload-time = "2026-05-18T04:31:42.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/3dceea03545d2e5ddfd839f0ddd5e1cecbf1697b5a428d5ba11cef6af95d/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01", size = 570476, upload-time = "2026-05-18T04:31:03.071Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f2/d39a5450c3532092b91f81d274360e613c2371bc874a89c7a1a3c5e8d138/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8", size = 465650, upload-time = "2026-05-18T04:30:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/22/24/ed72f68cbc1333ca9b9f2200aa048bb6658ae41709bc1caad4310f4bdffd/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5", size = 456398, upload-time = "2026-05-18T04:30:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/0d/64/982ef4a4e5bab5b6e5b6becc8cd5e732f6130a78b855f0abec6439a9a135/watchfiles-1.2.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d", size = 465140, upload-time = "2026-05-18T04:31:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/95282abf4ed680b6096010bcfc30c5fa7a041fc5aa5a2ad17a2cc6c75bba/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c", size = 630259, upload-time = "2026-05-18T04:31:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/607c1de1530c4bdcf2cf1d1ecc2505ddba5d96bd43ba9f2b0e79876f850f/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906", size = 659859, upload-time = "2026-05-18T04:30:24.333Z" }, + { url = "https://files.pythonhosted.org/packages/fa/08/d9e2e0f9e8e6791d33aefc694ad7eefa7f901f63caff84a81ded38692f9c/watchfiles-1.2.0-cp312-cp312-win32.whl", hash = "sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898", size = 275480, upload-time = "2026-05-18T04:30:31.307Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/9d42569c0102645cc8cea5d8c7d8a1e9d4ada2cb7f05f75e554b8aa2202a/watchfiles-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379", size = 288718, upload-time = "2026-05-18T04:32:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/0a/26/88e0dc6ee3898169d7fa22bb6a69cabf2502d2ee25cb8c876d1262d204f8/watchfiles-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5", size = 281026, upload-time = "2026-05-18T04:30:22.23Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/362702539275019a54dd2e94511b31a9b89c5f9e6a21966de7eb692549fc/watchfiles-1.2.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374", size = 400109, upload-time = "2026-05-18T04:31:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/71d5ba62db781e5587bded1d944c675374bc4aa37ff33d5018d98e8b6538/watchfiles-1.2.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65", size = 392167, upload-time = "2026-05-18T04:31:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/c66dd95d0423fe30d31820e2d1d5bda773764131bbb6ac0cb1cf303ac328/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69", size = 452372, upload-time = "2026-05-18T04:31:00.836Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/2fe99557e72f85627c6a8eed50d889e8d101623e060a22ad75b875cb932d/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579", size = 459596, upload-time = "2026-05-18T04:31:34.96Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/d4acfa0023367428ed48351b3b9b267893037b6cadae55620c61c24bcfd4/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7", size = 490869, upload-time = "2026-05-18T04:31:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/3164cbdce06c9fb95c4f7b9e2f9760b5e2797af43a9ecc317ef42a23a278/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2", size = 571641, upload-time = "2026-05-18T04:32:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/85d3731c55e65cd7690f3f803d24c139588aaf863e4bf2148fe7a7fa1a19/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6", size = 464444, upload-time = "2026-05-18T04:30:34.298Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/562641012b8b09872742c3b8adf9629ec479fd78f8d68ae4a0c13da8add6/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4", size = 453593, upload-time = "2026-05-18T04:31:23.464Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/cb8ef3d6f929d14158fdaaad9925985b7310abc9384dcd4d82dd0016fb59/watchfiles-1.2.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488", size = 465096, upload-time = "2026-05-18T04:31:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/80908e835e100527a9267147b08c0eee1fa6ab0ffec15edc04d1d44885f7/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb", size = 630638, upload-time = "2026-05-18T04:30:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/95ab2f256bb4af3cb2eb23b9317bda984ee6e0f11733a5c004a6c95b06e3/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377", size = 657684, upload-time = "2026-05-18T04:31:32.027Z" }, +] + +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, +] + +[[package]] +name = "xgrammar" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "apache-tvm-ffi" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "pydantic" }, + { name = "torch" }, + { name = "transformers" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/ea/6394caddd078d33772070eefaaf77cb0a826a3047b908b688dece7d040b5/xgrammar-0.2.1.tar.gz", hash = "sha256:4c48c251b75d211e9ffa7f4f4ac8b5b0164f89fd5f0d1883ad7ff4554922030d", size = 2427065, upload-time = "2026-05-17T21:39:26.576Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/87/af20928af1c7773b1d064743de0f291698dd531e5deee19fcda388c3495e/xgrammar-0.2.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0560568654e7745a80715dd87457f1feb3a4edf9d3894e47ab5c967f5d799ade", size = 23290997, upload-time = "2026-05-17T21:37:47.413Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e3/9b803c8168290421b3e79b001a823a7e381039fcdd349999c2eeaf454989/xgrammar-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:29756a266a62da398151946f3d912a5b737df2000a7023696449f9eec0996208", size = 23202397, upload-time = "2026-05-17T21:37:50.987Z" }, + { url = "https://files.pythonhosted.org/packages/44/60/7c6194b66e043f36a7fbdbf7e6e0e4c94b151f7e53ad1b197f53e711f5d1/xgrammar-0.2.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e8dd9853958a263b4015ce79133a0ff4eaa9d22ef781fb2350c7dfc40c2c012", size = 44218929, upload-time = "2026-05-17T21:37:56.155Z" }, + { url = "https://files.pythonhosted.org/packages/96/4b/327b3cf702b685a2be28d15490faa4beeac00c4fbcf9bb2d7db0fda32931/xgrammar-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbc6014dc1c92fc317b14519121c8163fe35fd934179e5a45d83f780ff231826", size = 44678489, upload-time = "2026-05-17T21:38:00.791Z" }, + { url = "https://files.pythonhosted.org/packages/57/39/69a5ba4dfa5e11a36265f69de3e16ee65ac9e77840c010056a2d8e99a875/xgrammar-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:9bd16e92b4385cb5ded48d65de80e4ee871b6b427b2ded99ac5028b907bf870f", size = 7409599, upload-time = "2026-05-17T21:38:03.829Z" }, + { url = "https://files.pythonhosted.org/packages/58/57/31d118a787debf1a0ab9c6a632e17373bffbaca65250c036498f58934246/xgrammar-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2e1964e2a1a5f59e138205996b8b41128c2f385a4de952292e8e47add556aa3c", size = 23202388, upload-time = "2026-05-17T21:38:07.476Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/8b5a8f103818a2a2b779c6ca21b5cda3536c78800bf25c6a37f87b130877/xgrammar-0.2.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb015d246a8c87bf46b11759442924cd6f96baf3ba444062a98e9a45ec5a1021", size = 44218981, upload-time = "2026-05-17T21:38:13.295Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/25ddd211f073a9db8299bdfec4534874d5d5d5f69499bb0c2ce9bf75f483/xgrammar-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba024024a454f31d3cb88679a1b073eba0133860f14525f1f5856fa46dec5228", size = 44678423, upload-time = "2026-05-17T21:38:18.812Z" }, + { url = "https://files.pythonhosted.org/packages/98/67/f99c7a0cd6221d6db6b67d7d987ef7bbb99e52bdd97d6d7d515e28c57e64/xgrammar-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:58ab4d1b2a9f23e38e9a53ceb30ae5c8363e9a9e14d686bbb9f06147fb2e050c", size = 7409601, upload-time = "2026-05-17T21:38:22.229Z" }, + { url = "https://files.pythonhosted.org/packages/4c/19/f0e55d028ca67d8862fad73ba15e98c684596269b3ff3120377f37a7563a/xgrammar-0.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:5206c23b383e5a6d7743792cf85da4aee76dfb9f11a202cb15d08cc017ca87a3", size = 23291012, upload-time = "2026-05-17T21:38:25.516Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cc/560094347c26b35c750a9ffda1082b941b783890f7ff026aea2a6dcfa0f7/xgrammar-0.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4f8c7b50130f2d09cf19c43f40a4a7ef0421d14e73b68778d8d2ad50d8fb054b", size = 23202382, upload-time = "2026-05-17T21:38:29.634Z" }, + { url = "https://files.pythonhosted.org/packages/7f/52/6e8de281657b27d9d18ad0ebf12de33b6ed43634eff1b0508e319e937a49/xgrammar-0.2.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34b7564e4074e41ec670973db92a7b8432e2095922db4389267a60da60824c40", size = 44218983, upload-time = "2026-05-17T21:38:34.506Z" }, + { url = "https://files.pythonhosted.org/packages/96/33/ad8df7d7bfb1b0c7d75748ad89ea0cd3d8f06a7835eab952c10abb7dfdae/xgrammar-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffb35a49df2c825e706bd559a9d0bd59b1772a58c9aa90fbf9d7a929634048d", size = 44678378, upload-time = "2026-05-17T21:38:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d5/1d9455840cc503a3150f2cd66583eef8a9bc95ef831f4ab703e210c41491/xgrammar-0.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:5831ae6e0c8a14b151e3a0d4cd2158b4442f498241aef0cb3ed6f5a8af874057", size = 7504315, upload-time = "2026-05-17T21:38:43.081Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ec/a84e6c091060cb6799125e5a1b97041cf6b132bbf7dd6b54be42998e46c2/xgrammar-0.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b2cbea6a93837ee81a2524839c93ccfe7da40f7cae4e9ee4859fb4a5a60433ab", size = 23291000, upload-time = "2026-05-17T21:38:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e4/dee8d13dde3a61d757d3671d36ec9f4a11ad3a6cf629667838f3a1b42e1a/xgrammar-0.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0cd55c698f2859053009ea9e1040ae1503265a3db717ce0580ca286663fe5da9", size = 23202341, upload-time = "2026-05-17T21:38:50.908Z" }, + { url = "https://files.pythonhosted.org/packages/9d/07/35d5587604357fcdb831fd47aba9fe4557dea9fde636c05478830780f012/xgrammar-0.2.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a3569ca924eaf0ac2380c3c421dfd08eefa0658ef000e7ae7545c7499c7b36c", size = 44218815, upload-time = "2026-05-17T21:38:55.37Z" }, + { url = "https://files.pythonhosted.org/packages/37/99/9704f02c9a0320163fab9351c17c62b980f8e885dc530d998591ced84ed2/xgrammar-0.2.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fff6a09f844e6bbb5e4bd9cbc0bd5e07e21962c54b661dfeb2ec4ecd96e0eded", size = 44678554, upload-time = "2026-05-17T21:39:00.098Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d1/acb2d68930535364453669af1a3b0f3f95f647f4bddb9ea20c5fab295672/xgrammar-0.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:743196d955b82181af265c358a84b9e1e80ea0e368f129e560c898555a8df9f6", size = 7504318, upload-time = "2026-05-17T21:39:03.528Z" }, +] + +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644, upload-time = "2026-04-25T11:08:00.658Z" }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003, upload-time = "2026-04-25T11:08:02.338Z" }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655, upload-time = "2026-04-25T11:08:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664, upload-time = "2026-04-25T11:08:05.011Z" }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317, upload-time = "2026-04-25T11:08:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457, upload-time = "2026-04-25T11:08:08.099Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894, upload-time = "2026-04-25T11:08:09.429Z" }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409, upload-time = "2026-04-25T11:08:10.91Z" }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135, upload-time = "2026-04-25T11:08:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379, upload-time = "2026-04-25T11:08:14.206Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447, upload-time = "2026-04-25T11:08:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660, upload-time = "2026-04-25T11:08:17.441Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076, upload-time = "2026-04-25T11:08:19.134Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990, upload-time = "2026-04-25T11:08:20.618Z" }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590, upload-time = "2026-04-25T11:08:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442, upload-time = "2026-04-25T11:08:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356, upload-time = "2026-04-25T11:08:25.99Z" }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898, upload-time = "2026-04-25T11:08:27.608Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519, upload-time = "2026-04-25T11:08:29.301Z" }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191, upload-time = "2026-04-25T11:08:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604, upload-time = "2026-04-25T11:08:32.862Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271, upload-time = "2026-04-25T11:08:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284, upload-time = "2026-04-25T11:08:35.987Z" }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701, upload-time = "2026-04-25T11:08:37.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646, upload-time = "2026-04-25T11:08:39.109Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125, upload-time = "2026-04-25T11:08:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633, upload-time = "2026-04-25T11:08:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899, upload-time = "2026-04-25T11:08:43.645Z" }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116, upload-time = "2026-04-25T11:08:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012, upload-time = "2026-04-25T11:08:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534, upload-time = "2026-04-25T11:08:49.149Z" }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217, upload-time = "2026-04-25T11:08:50.805Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906, upload-time = "2026-04-25T11:08:52.418Z" }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057, upload-time = "2026-04-25T11:08:54.105Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886, upload-time = "2026-04-25T11:08:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015, upload-time = "2026-04-25T11:08:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457, upload-time = "2026-04-25T11:08:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738, upload-time = "2026-04-25T11:09:01.423Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127, upload-time = "2026-04-25T11:09:03.592Z" }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962, upload-time = "2026-04-25T11:09:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643, upload-time = "2026-04-25T11:09:08.153Z" }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522, upload-time = "2026-04-25T11:09:09.534Z" }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807, upload-time = "2026-04-25T11:09:11.234Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901, upload-time = "2026-05-19T21:29:20.014Z" }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229, upload-time = "2026-05-19T21:29:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978, upload-time = "2026-05-19T21:29:23.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733, upload-time = "2026-05-19T21:29:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113, upload-time = "2026-05-19T21:29:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899, upload-time = "2026-05-19T21:29:28.842Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862, upload-time = "2026-05-19T21:29:31.086Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060, upload-time = "2026-05-19T21:29:32.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613, upload-time = "2026-05-19T21:29:34.379Z" }, + { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012, upload-time = "2026-05-19T21:29:36.216Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887, upload-time = "2026-05-19T21:29:38.746Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620, upload-time = "2026-05-19T21:29:40.368Z" }, + { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599, upload-time = "2026-05-19T21:29:42.66Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604, upload-time = "2026-05-19T21:29:44.632Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161, upload-time = "2026-05-19T21:29:46.755Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619, upload-time = "2026-05-19T21:29:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362, upload-time = "2026-05-19T21:29:51Z" }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667, upload-time = "2026-05-19T21:29:52.743Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069, upload-time = "2026-05-19T21:29:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670, upload-time = "2026-05-19T21:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916, upload-time = "2026-05-19T21:29:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625, upload-time = "2026-05-19T21:30:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574, upload-time = "2026-05-19T21:30:02.544Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534, upload-time = "2026-05-19T21:30:04.319Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481, upload-time = "2026-05-19T21:30:05.988Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529, upload-time = "2026-05-19T21:30:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338, upload-time = "2026-05-19T21:30:09.713Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147, upload-time = "2026-05-19T21:30:11.365Z" }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272, upload-time = "2026-05-19T21:30:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962, upload-time = "2026-05-19T21:30:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063, upload-time = "2026-05-19T21:30:16.683Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438, upload-time = "2026-05-19T21:30:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458, upload-time = "2026-05-19T21:30:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589, upload-time = "2026-05-19T21:30:23.412Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424, upload-time = "2026-05-19T21:30:25.425Z" }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690, upload-time = "2026-05-19T21:30:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248, upload-time = "2026-05-19T21:30:29.297Z" }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084, upload-time = "2026-05-19T21:30:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272, upload-time = "2026-05-19T21:30:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497, upload-time = "2026-05-19T21:30:34.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002, upload-time = "2026-05-19T21:30:37.724Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524, upload-time = "2026-05-19T21:30:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165, upload-time = "2026-05-19T21:30:41.888Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010, upload-time = "2026-05-19T21:30:43.985Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128, upload-time = "2026-05-19T21:30:46.291Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382, upload-time = "2026-05-19T21:30:48.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964, upload-time = "2026-05-19T21:30:49.785Z" }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204, upload-time = "2026-05-19T21:30:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510, upload-time = "2026-05-19T21:30:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584, upload-time = "2026-05-19T21:30:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410, upload-time = "2026-05-19T21:30:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980, upload-time = "2026-05-19T21:30:59.735Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219, upload-time = "2026-05-19T21:31:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, +] + +[[package]] +name = "z3-solver" +version = "4.15.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/8e/0c8f17309549d2e5cde9a3ccefa6365437f1e7bafe71878eaf9478e47b18/z3_solver-4.15.4.0.tar.gz", hash = "sha256:928c29b58c4eb62106da51c1914f6a4a55d0441f8f48a81b9da07950434a8946", size = 5018600, upload-time = "2025-10-29T18:12:03.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/33/a3d5d2eaeb0f7b3174d57d405437eabb2075d4d50bd9ea0957696c435c7b/z3_solver-4.15.4.0-py3-none-macosx_13_0_arm64.whl", hash = "sha256:407e825cc9211f95ef46bdc8d151bf630e7ab2d62a21d24cd74c09cc5b73f3aa", size = 37052538, upload-time = "2025-10-29T18:11:46.233Z" }, + { url = "https://files.pythonhosted.org/packages/47/84/fd7ffac1551cd9f8d44fe41358f738be670fc4c24dfd514fab503f2cf3e7/z3_solver-4.15.4.0-py3-none-macosx_13_0_x86_64.whl", hash = "sha256:00bd10c5a6a5f6112d3a9a810d0799227e52f76caa860dafa5e00966bb47eb13", size = 39807925, upload-time = "2025-10-29T18:11:49.81Z" }, + { url = "https://files.pythonhosted.org/packages/21/c9/bb51a96af0091324c81b803f16c49f719f9f6ea0b0bb52200f5c97ec4892/z3_solver-4.15.4.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e103a6f203f505b8b8b8e5c931cc407c95b61556512d4921c1ddc0b3f41b08e", size = 29268352, upload-time = "2025-10-29T18:11:53.032Z" }, + { url = "https://files.pythonhosted.org/packages/bf/2e/0b49f7e4e53817cfb09a0f6585012b782dfe0b666e8abefcb4fac0570606/z3_solver-4.15.4.0-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:62c7e9cbdd711932301f29919ad9158de9b2f58b4d281dd259bbcd0a2f408ba1", size = 27226534, upload-time = "2025-10-29T18:11:55.59Z" }, + { url = "https://files.pythonhosted.org/packages/26/91/33de49538444d4aafbe47415c450c2f9abab1733e1226f276b496672f46c/z3_solver-4.15.4.0-py3-none-win32.whl", hash = "sha256:be3bc916545c96ffbf89e00d07104ff14f78336e55db069177a1bfbcc01b269d", size = 13191672, upload-time = "2025-10-29T18:11:58.424Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/a0b135e4419df475177ae78fc93c422430b0fd8875649486f9a5989772e6/z3_solver-4.15.4.0-py3-none-win_amd64.whl", hash = "sha256:00e35b02632ed085ea8199fb230f6015e6fc40554a6680c097bd5f060e827431", size = 16259597, upload-time = "2025-10-29T18:12:01.14Z" }, +] From c6612b37b188223c2ff9f65fc8c8f957cafd493a Mon Sep 17 00:00:00 2001 From: swappy <59965507+rycerzes@users.noreply.github.com> Date: Mon, 1 Jun 2026 12:46:56 +0530 Subject: [PATCH 79/79] refactor: loss_mask/prefix-merging fix - remove duplicate response content --- .../mini_swe_env/async_grpo/rollout_worker.py | 236 +++++++++--------- 1 file changed, 121 insertions(+), 115 deletions(-) diff --git a/examples/mini_swe_env/async_grpo/rollout_worker.py b/examples/mini_swe_env/async_grpo/rollout_worker.py index 91b1728c1..7c52964e1 100644 --- a/examples/mini_swe_env/async_grpo/rollout_worker.py +++ b/examples/mini_swe_env/async_grpo/rollout_worker.py @@ -673,9 +673,16 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None all_lps: list[float] = [] initial_prompt_ids: list[int] | None = None - # prev_base_ids: render of all messages so far WITHOUT add_generation_prompt. - # The training template guarantees this is prefix-preserving across turns. - prev_base_ids: list[int] | None = None + # prev_prompt_ids: the FULL prompt (with add_generation_prompt=True) + # used for the most recently completed turn. Used for EOT-based + # interstitial extraction following Polar's prefix_merging approach + # (arXiv:2605.24220 §3.4.2). The next turn's canonical_tail = + # next_prompt[len(prev_prompt_ids):] contains the canonical response + # copy + interstitial; we split at EOT to get only the interstitial. + prev_prompt_ids: list[int] | None = None + # The raw turn_ids from the most recent generation, needed to detect + # whether the response already ends with EOT (for correct slicing). + prev_turn_ids: list[int] | None = None turns = 0 answer_called = False @@ -722,44 +729,52 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None all_ids.extend(current_prompt_ids) all_mask.extend([0] * len(current_prompt_ids)) all_lps.extend([0.0] * len(current_prompt_ids)) - elif prev_base_ids is not None: + elif prev_prompt_ids is not None: # Subsequent turns: compute the interstitial (tool-result # suffix) between the last generation and this turn's prompt. # - # We use Polar's approach (arXiv:2605.24220 §3.4.2): - # The "base" render (add_generation_prompt=False) is - # prefix-preserving across turns — guaranteed by TRL's - # training chat template. The suffix = tokens in the - # current prompt that come AFTER the previous base. - prev_len = len(prev_base_ids) - - # Prefix validation. - if current_prompt_ids[:prev_len] != prev_base_ids: - _log.warning( - "prefix mismatch at turn %d (prev_base=%d tokens, " - "first diff at idx %d). Using EOT fallback.", - turns, - prev_len, - next( - ( - i - for i, (a, b) in enumerate( - zip(current_prompt_ids, prev_base_ids) - ) - if a != b - ), - -1, - ), - ) - # Fallback: use Polar's EOT-based interstitial detection - # on the canonical tail from the full prompt. - suffix_ids = _slice_interstitial_fallback( - current_prompt_ids, - prev_base_ids, - self._tokenizer.eos_token_id, - ) + # Following Polar's prefix_merging (arXiv:2605.24220 §3.4.2) + # and TRL's _get_tool_suffix_ids approach: + # + # canonical_tail = next_prompt[len(prev_prompt):] contains: + # 1. Canonical copy of prev response (may differ from raw + # turn_ids due to BPE non-canonicality) + # 2. EOT token (<|im_end|>) + # 3. True interstitial (tool result, user msg, gen prompt) + # + # We split at EOT to extract only the interstitial (#3), + # avoiding the ~2× sequence bloat from duplicating responses. + # + # If the prefix breaks (due to message truncation changing + # earlier content between turns), we find the longest common + # prefix first, then EOT-split the remainder. + prev_len = len(prev_prompt_ids) + if ( + len(current_prompt_ids) >= prev_len + and current_prompt_ids[:prev_len] == prev_prompt_ids + ): + canonical_tail = current_prompt_ids[prev_len:] else: - suffix_ids = current_prompt_ids[prev_len:] + # Message truncation changed earlier content — find + # the actual divergence point. + common_len = 0 + for a, b in zip(current_prompt_ids, prev_prompt_ids): + if a != b: + break + common_len += 1 + canonical_tail = current_prompt_ids[common_len:] + if common_len < prev_len: + _log.debug( + "prefix diverged at turn %d: expected %d, " + "common=%d (likely truncation drift)", + turns, prev_len, common_len, + ) + + suffix_ids = _extract_interstitial_after_eot( + canonical_tail=canonical_tail, + prev_turn_ids=prev_turn_ids, + eot_id=self._tokenizer.eos_token_id, + ) all_ids.extend(suffix_ids) all_mask.extend([0] * len(suffix_ids)) @@ -794,16 +809,16 @@ async def _rollout(self, task: SWETask, episode_id: str) -> RolloutSample | None all_mask.extend([1] * len(turn_ids)) all_lps.extend(turn_lps) - # For next turn's suffix computation: compute the "base" render - # of all messages so far (WITHOUT add_generation_prompt). This - # is prefix-preserving across turns — the next turn's prompt - # (rendered with add_gen=True) will start with these tokens. + # For next turn's suffix computation: save this turn's full + # prompt (with add_generation_prompt=True) as the reference. + # The next turn's prompt will extend this prefix (same messages + # plus the response + tool result), so canonical_tail = + # next_prompt[len(prev_prompt):] gives us the new content. # - # This approach avoids retokenization drift at the - # add_generation_prompt boundary (e.g. \n vs - # \n\n\n\n) which would cause prefix mismatches - # with the naive prev_prompt = prompt + turn_ids approach. - prev_base_ids = self._render_base_ids(messages, tools) + # This is Polar/TRL's approach: comparing canonical-vs-canonical + # server tokenizations avoids BPE drift from raw tokens. + prev_prompt_ids = current_prompt_ids + prev_turn_ids = turn_ids # ── Build chat response for Pi ──────────────────── assistant_message = _parse_assistant_message( @@ -931,38 +946,6 @@ def _render_prompt_ids( ids = self._tokenizer.apply_chat_template(messages, **kwargs) return cast(list[int], ids) - def _render_base_ids( - self, - messages: list[dict[str, Any]], - tools: list[dict[str, Any]] | None, - ) -> list[int]: - """Render messages WITHOUT add_generation_prompt. - - The resulting token sequence is prefix-preserving: appending tool - results to the messages and re-rendering (with add_gen=True) will - produce a token sequence whose first ``len(base_ids)`` tokens match - exactly. This property is what makes suffix computation reliable - across turns (see TITO blog, Polar §3.4.2, TRL's - ``is_chat_template_prefix_preserving``). - """ - kwargs: dict[str, Any] = { - "add_generation_prompt": False, - "return_dict": False, - } - if tools: - kwargs["tools"] = tools - if self._chat_template: - kwargs["chat_template"] = self._chat_template - - messages = _ensure_tool_call_arguments_parsed(messages) - - try: - ids = self._tokenizer.apply_chat_template(messages, **kwargs) - except TypeError: - kwargs.pop("tools", None) - ids = self._tokenizer.apply_chat_template(messages, **kwargs) - return cast(list[int], ids) - class ContextOverflowError(RuntimeError): """Raised when prompt + max_tokens exceeds max_model_len. @@ -1031,47 +1014,70 @@ def _generate( # ── Helpers ──────────────────────────────────────────────────────────── -def _slice_interstitial_fallback( - current_prompt_ids: list[int], - prev_base_ids: list[int], - eos_token_id: int | None, +def _extract_interstitial_after_eot( + *, + canonical_tail: list[int], + prev_turn_ids: list[int] | None, + eot_id: int | None, ) -> list[int]: - """Fallback interstitial extraction when prefix check fails. - - Uses Polar's EOT-based detection: find the longest common prefix between - ``current_prompt_ids`` and ``prev_base_ids``, then return everything in - ``current_prompt_ids`` after that common prefix. This handles edge cases - where the template isn't perfectly prefix-preserving (e.g. thinking-token - variations). + """Extract the true interstitial from a canonical tail using EOT splitting. + + Following Polar's ``PrefixMergingBuilder._slice_interstitial`` and TRL's + ``_get_tool_suffix_ids`` EOS-trimming approach: + + ``canonical_tail`` = next_prompt[len(prev_prompt):] contains: + 1. Canonical re-rendering of the previous assistant response + 2. EOT token (``<|im_end|>`` for Qwen/ChatML) + 3. True interstitial (tool result, user turn, gen prompt) + + We split at the first EOT to skip the canonical response copy (#1-#2) + and return only the interstitial (#3). + + If ``prev_turn_ids`` already ends with EOT (natural stop), the EOT in + the canonical tail is a duplicate — skip it. Otherwise (truncation), + include it so the stream closes the assistant turn. + + Parameters + ---------- + canonical_tail: + Tokens from next_prompt that come after prev_prompt. + prev_turn_ids: + The raw response token IDs from the previous generation. + eot_id: + The end-of-turn token ID (e.g. ``<|im_end|>`` / eos_token_id). + + Returns + ------- + list[int] + Only the interstitial tokens (tool results, user messages, + generation prompt for the next turn). """ - # Find longest common prefix - common_len = 0 - for a, b in zip(current_prompt_ids, prev_base_ids): - if a != b: - break - common_len += 1 - - # Return everything after the common prefix in current_prompt_ids - return current_prompt_ids[common_len:] + if not canonical_tail: + return canonical_tail + if eot_id is None: + raise ValueError( + "Cannot extract interstitial without eot_id (tokenizer.eos_token_id). " + "This would duplicate the previous response in the training stream." + ) -def _strip_trailing_eos(ids: list[int], eos_token_id: int | None) -> list[int]: - """Strip trailing EOS token from generated token IDs. - - vLLM includes the stop token (e.g. ``<|im_end|>``) in the returned - ``token_ids`` when ``finish_reason == 'stop'``. The chat template will - re-render that same token as part of the canonical assistant block - (``{{- '<|im_end|>\n' }}``). To avoid double-counting it in the prefix - computation, we strip it here. - - This mirrors TRL's ``_get_tool_suffix_ids`` EOS-trimming logic and Polar's - interstitial boundary detection (Section 3.4.2 of arXiv:2605.24220). - """ - if eos_token_id is None or not ids: - return ids - if ids[-1] == eos_token_id: - return ids[:-1] - return ids + # Find the first EOT in the canonical tail. This marks the end of the + # canonical copy of the previous response. + try: + eot_pos = canonical_tail.index(eot_id) + except ValueError: + # No EOT found — edge case (e.g. truncated response without stop token). + # Return full tail as interstitial (conservative). + return canonical_tail + + # If the raw response already ended with EOT (model emitted stop token), + # the EOT in canonical_tail is a duplicate — skip past it. + # Otherwise (response was truncated), include the EOT to properly close + # the assistant turn in the training stream. + if prev_turn_ids and prev_turn_ids[-1] == eot_id: + return canonical_tail[eot_pos + 1:] + else: + return canonical_tail[eot_pos:] def _get_messages(intercept: dict[str, Any]) -> list[dict[str, Any]]: