From fb071fb28b3dee7e5f9d45336923a0064b83bc85 Mon Sep 17 00:00:00 2001 From: Niels Bantilan Date: Fri, 21 Aug 2026 00:31:58 -0400 Subject: [PATCH 1/2] feat(plugins): add DeepSeek Harness agent adapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `flyteplugins-agents-deepseek`, running DeepSeek Harness (`deepseek-harness-sdk`) agents on Flyte. It follows the shared agent-adapter contract — `tool` + `run_agent`/`run_agent_sync` with the standard keyword surface — and passes `assert_adapter_conforms`. Tools need a different mechanism here than in the client-side SDKs. The harness has no tool-registration message: its wire protocol is `initialize` / `session/prompt`, and its tool surface is whatever its Cordis composition provides inside the runtime subprocess (its in-process `harness.registerTool` is a TypeScript plugin API the Python SDK can't reach). What every composition does provide is local bash in a workspace we choose, so each Flyte-task tool is published there as an executable shim that calls back over a Unix socket into `task.aio(...)` — a durable child action with its own container/resources, retries and caching. The tool manual rides on the prompt, since there is no tool-declaration channel either. `DeepSeekHarness.run` is blocking, so it is driven via `asyncio.to_thread`; that is what keeps the event loop free to serve tool calls while the agent works. Durability is session resume rather than per-turn replay (the model loop is in a subprocess Flyte doesn't intercept, as with the Claude adapter): the harness's JSONL session store is mirrored onto a `flyte.Checkpoint`, keyed by a session id derived from the task's action, so a retry continues the conversation. With `memory_key` the same archive is kept in a keyed `MemoryStore` instead, giving cross-run memory and subsuming crash-resume. Also renders the run timeline into the task report, mapping the runtime's session events (assistant turns with token usage, `tool/call` / `tool/result`, `turn/end`) plus the bridge's per-tool outcomes. Includes 61 tests — the bridge ones run the published shim as a real subprocess, covering shim -> socket -> `task.aio` end to end, including concurrent calls and tool failures — plus six examples and the adapter README. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/publish.yml | 1 + plugins/agents/README.md | 5 +- plugins/agents/deepseek/README.md | 173 ++ .../examples/deepseek_crash_resume.py | 85 + .../examples/deepseek_custom_agent.py | 71 + .../examples/deepseek_durable_agent.py | 82 + .../deepseek/examples/deepseek_memory.py | 65 + .../deepseek/examples/deepseek_multi_agent.py | 119 ++ .../examples/deepseek_workspace_agent.py | 130 ++ plugins/agents/deepseek/pyproject.toml | 63 + .../flyteplugins/agents/deepseek/__init__.py | 29 + .../flyteplugins/agents/deepseek/_bridge.py | 251 +++ .../flyteplugins/agents/deepseek/_durable.py | 164 ++ .../flyteplugins/agents/deepseek/_memory.py | 88 + .../src/flyteplugins/agents/deepseek/_run.py | 360 ++++ .../flyteplugins/agents/deepseek/_tools.py | 165 ++ plugins/agents/deepseek/tests/__init__.py | 0 .../agents/deepseek/tests/test_conformance.py | 14 + .../deepseek/tests/test_deepseek_bridge.py | 204 +++ .../deepseek/tests/test_deepseek_durable.py | 173 ++ .../deepseek/tests/test_deepseek_memory.py | 112 ++ .../deepseek/tests/test_deepseek_run.py | 378 ++++ .../deepseek/tests/test_deepseek_tools.py | 140 ++ plugins/agents/deepseek/uv.lock | 1513 +++++++++++++++++ 24 files changed, 4383 insertions(+), 2 deletions(-) create mode 100644 plugins/agents/deepseek/README.md create mode 100644 plugins/agents/deepseek/examples/deepseek_crash_resume.py create mode 100644 plugins/agents/deepseek/examples/deepseek_custom_agent.py create mode 100644 plugins/agents/deepseek/examples/deepseek_durable_agent.py create mode 100644 plugins/agents/deepseek/examples/deepseek_memory.py create mode 100644 plugins/agents/deepseek/examples/deepseek_multi_agent.py create mode 100644 plugins/agents/deepseek/examples/deepseek_workspace_agent.py create mode 100644 plugins/agents/deepseek/pyproject.toml create mode 100644 plugins/agents/deepseek/src/flyteplugins/agents/deepseek/__init__.py create mode 100644 plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_bridge.py create mode 100644 plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_durable.py create mode 100644 plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_memory.py create mode 100644 plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_run.py create mode 100644 plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_tools.py create mode 100644 plugins/agents/deepseek/tests/__init__.py create mode 100644 plugins/agents/deepseek/tests/test_conformance.py create mode 100644 plugins/agents/deepseek/tests/test_deepseek_bridge.py create mode 100644 plugins/agents/deepseek/tests/test_deepseek_durable.py create mode 100644 plugins/agents/deepseek/tests/test_deepseek_memory.py create mode 100644 plugins/agents/deepseek/tests/test_deepseek_run.py create mode 100644 plugins/agents/deepseek/tests/test_deepseek_tools.py create mode 100644 plugins/agents/deepseek/uv.lock diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ab8664b47..828ea1036 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -266,6 +266,7 @@ jobs: - "plugins/agents/langgraph" - "plugins/agents/pydantic_ai" - "plugins/agents/hermes" + - "plugins/agents/deepseek" - "plugins/lance" include: - workdir: "plugins/sglang" diff --git a/plugins/agents/README.md b/plugins/agents/README.md index a4fc415d3..4d29b19b0 100644 --- a/plugins/agents/README.md +++ b/plugins/agents/README.md @@ -19,9 +19,10 @@ Each SDK is a separate, co-located package on a shared core: | [`flyteplugins.agents.crewai`](crewai/) | `flyteplugins-agents-crewai` | [CrewAI](https://docs.crewai.com/) | | [`flyteplugins.agents.pydantic_ai`](pydantic_ai/) | `flyteplugins-agents-pydantic-ai` | [Pydantic AI](https://ai.pydantic.dev/) (2.x) | | [`flyteplugins.agents.hermes`](hermes/) | `flyteplugins-agents-hermes` | [Hermes Agent](https://pypi.org/project/hermes-agent/) (Nous Research; Python ≥3.11) | +| [`flyteplugins.agents.deepseek`](deepseek/) | `flyteplugins-agents-deepseek` | [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/en/guide/python-sdk) (`deepseek-harness-sdk`) | ```bash -pip install flyteplugins-agents-openai # or -claude / -mistral / -google / -deepagents / -langchain / -langgraph / -crewai / -pydantic-ai +pip install flyteplugins-agents-openai # or -claude / -mistral / -google / -deepagents / -langchain / -langgraph / -crewai / -pydantic-ai / -deepseek ``` Each adapter has its own README (linked above) with the SDK-specific details. @@ -38,7 +39,7 @@ across SDKs: - Each model turn is recorded for replay by tracing the seam below the SDK's loop (`durable=True`), so a crashed/retried run replays completed turns instead of re-calling (and re-billing) the model. (Where the SDK runs its loop in a subprocess - — Claude — durability is the SDK's own session-resume instead.) + — Claude, DeepSeek Harness — durability is the SDK's own session-resume instead.) - Cross-run memory via `memory_key` — the conversation continues across separate runs and workers, backed by a durable keyed store. diff --git a/plugins/agents/deepseek/README.md b/plugins/agents/deepseek/README.md new file mode 100644 index 000000000..a8af2cae2 --- /dev/null +++ b/plugins/agents/deepseek/README.md @@ -0,0 +1,173 @@ +# flyteplugins-agents-deepseek + +Run [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/en/guide/python-sdk) +agents on Flyte. You keep writing DeepSeek Harness code; Flyte is the runtime +underneath. + +```bash +pip install flyteplugins-agents-deepseek +``` + +```python +import flyte +from flyteplugins.agents.deepseek import tool, run_agent + +env = flyte.TaskEnvironment( + "deepseek-agent", + secrets=[flyte.Secret(key="deepseek_api_key", as_env_var="DEEPSEEK_API_KEY")], +) + + +@tool +@env.task(cache="auto", retries=3) +async def get_weather(city: str) -> str: + """Get the current weather for a city.""" + return f"The weather in {city} is sunny, 22°C." + + +@env.task(report=True, retries=3) +async def city_agent(question: str) -> str: + return await run_agent(question, tools=[get_weather], model="deepseek-v4-flash") +``` + +## How it maps to Flyte + +- The loop runs inside the harness runtime subprocess (JSON-RPC over stdio). + `run_agent` drives it from inside your `@env.task` — the blocking + `DeepSeekHarness.run` is bridged off the event loop with `asyncio.to_thread`, so + the task stays responsive while the agent works. +- Tools are Flyte tasks. When the agent calls one it runs as a durable Flyte child + action (its own container/resources, retries, caching). The input schema is + derived via the Flyte type engine. +- `workspace=` points the harness's own tools (bash, string editor) at a real + directory — for example a `flyte.io.Dir` you downloaded — so the agent can read + and edit actual files and you can hand the result downstream as an artifact. + +### How tools work here + +This adapter differs from the client-side ones, and it's worth knowing why. + +DeepSeek Harness has **no tool-registration channel**: its wire protocol is +`initialize` / `session/prompt`, and its tool surface is whatever its Cordis +plugin composition provides *inside the runtime subprocess*. There is nowhere to +hand a Python function. + +What every composition does provide is local bash, scoped to a working directory +the adapter chooses. So the bridge meets it there: + +1. each tool is published into `/.flyte_tools/` as a small + executable shim (stdlib-only Python, run under this process's own interpreter, + so the harness runtime needs nothing installed); +2. `run_agent` listens on a Unix domain socket in a private temp directory; +3. the model runs `.flyte_tools/get_weather '{"city": "Paris"}'`, the shim forwards + the JSON arguments over the socket, and the adapter awaits `task.aio(...)` — a + durable Flyte child action — before writing the result back for the shim to print. + +Because the harness has no tool-declaration message either, the tool manual +(names, parameter types, an example invocation per tool) is prepended to the +prompt. A failing tool comes back as a non-zero exit with the reason on stderr, so +the agent sees the error and can react rather than the run dying. + +The shims are the only thing the adapter writes into your workspace, and they are +removed when the run ends. + +If you own the Cordis composition, the runtime does have a first-class in-process +tool API (`harness.registerTool(ctx, tool)`) — but it is a TypeScript plugin API +inside the runtime, not something the Python SDK can reach, so it is not an option +for an adapter that has to work with the stock composition. + +## Durability + +Two layers, both real: + +- Tool calls are durable Flyte child actions (own container/resources, retries, + caching) — always, regardless of `durable`. +- The conversation survives a crash. With `durable=True`, `run_agent` mirrors the + harness's JSONL session store (`session_root` / `DSH_SESSION_ROOT`) onto a + `flyte.Checkpoint`. The session id is derived from the task's action, so it is + stable across retries; on a retry the prior attempt's transcript is restored into + the session root and the same session is prompted, so the harness continues the + conversation instead of starting over. + +We delegate to the harness's own session persistence because the model loop runs +in its runtime subprocess (which Flyte doesn't intercept), so a model turn can't be +a `flyte.trace` leaf the way it is for other client-side SDKs. Session resume is the +coarser-grained equivalent — whole-session, not per-turn — and it no-ops cleanly +when there's no checkpoint context (e.g. local runs). + +## Observability + +`run_agent` renders a timeline into the task report (`report=True`): assistant +turns and turn endings from the harness's streamed session events, the harness's +own tool activity, and each Flyte-task tool's arguments and result (or error), +recorded by the bridge as it dispatches them. + +Session notifications arrive on the worker thread running the blocking harness +call; each is marshalled back onto the event loop before touching the report, so +the timeline stays correctly ordered against the tool rows. + +## Memory + +Pass `memory_key` (a user/thread id) for cross-run memory — the agent resumes the +same conversation across separate runs: + +```python +await run_agent(message, model="deepseek-v4-flash", memory_key="user-alice") +``` + +The session archive is persisted to a durable, keyed `MemoryStore` and restored on +the next run with the same key, which then prompts the same harness session id. +That also covers crash-resume, so it supersedes the per-run `durable` checkpoint. + +## Bring your own configuration + +Pass a fully-built `DeepSeekHarnessConfig` as `config=` to keep SDK-native setup — +a custom `cordis` composition, `base_url` / `api_key`, timeouts. The adapter layers +only `cwd` (the workspace, where shims are published) and `session_root` (what +resume and memory mirror) on top; `model` / `provider` / `max_tokens` and any extra +keyword arguments override fields on it. + +## Runtime + +The `deepseek-harness-sdk` wheel pins the matching `deepseek-harness-runtime-bin` +platform wheel, which bundles the single-file `dsh-jsonrpc-agent` executable — so +`pip install flyteplugins-agents-deepseek` is all the runtime image needs (no +separate Node.js install). Wheels are published for Linux x86-64/aarch64 and +macOS 14+ arm64. + +Set `DEEPSEEK_API_KEY` in the environment (a Flyte secret, as above), and +`DEEPSEEK_BASE_URL` if you route through a proxy. + +The SDK is currently published as a pre-release, so installing it may require +`--prerelease=allow` (uv) / `--pre` (pip) depending on your resolver settings. + +## Examples + +See [`examples/`](examples/): + +- [`deepseek_durable_agent.py`](examples/deepseek_durable_agent.py) — a single + durable agent: tools as Flyte tasks, assistant turns and tool outcomes in the report, + in both the async and sync call forms. +- [`deepseek_crash_resume.py`](examples/deepseek_crash_resume.py) — crash & resume: + the task crashes on its first attempt; on retry the conversation resumes from the + `flyte.Checkpoint`-backed session and completed tool calls are cache hits. Run on a + backend to see resume. +- [`deepseek_workspace_agent.py`](examples/deepseek_workspace_agent.py) — a real + workspace: the agent reads, edits and tests an actual `flyte.io.Dir` with the + harness's bash and editor, verifies its own work through a Flyte-task tool, and the + patched directory becomes the task's output. +- [`deepseek_multi_agent.py`](examples/deepseek_multi_agent.py) — multi-agent + orchestration: a planner agent decomposes a topic, researcher agents fan out in + parallel, an editor agent synthesizes — each agent its own durable action. +- [`deepseek_memory.py`](examples/deepseek_memory.py) — cross-run memory: two + separate runs share a `memory_key`; the agent learns a fact in run 1 and recalls it + in run 2. +- [`deepseek_custom_agent.py`](examples/deepseek_custom_agent.py) — bring your own + `DeepSeekHarnessConfig` (custom Cordis composition, proxied endpoint, timeouts). + +## Conformance + +This adapter passes the shared `flyteplugins.agents.core.testing.assert_adapter_conforms` +check — the same one every adapter runs — so it follows the common format +(`tool` + `run_agent`, tool tasks wired to the resolver) despite a very different +underlying SDK shape. diff --git a/plugins/agents/deepseek/examples/deepseek_crash_resume.py b/plugins/agents/deepseek/examples/deepseek_crash_resume.py new file mode 100644 index 000000000..2634e5a51 --- /dev/null +++ b/plugins/agents/deepseek/examples/deepseek_crash_resume.py @@ -0,0 +1,85 @@ +"""Crash and resume: durable DeepSeek Harness agent recovery on Flyte. + +Shows that a crash mid-run does not restart the agent from scratch. On the first +attempt the agent does real work (model turns inside the harness runtime + durable +tool calls), then the worker is killed (simulated). Flyte retries the task; on the +second attempt: + +- the conversation resumes — with ``durable=True`` (the default) ``run_agent`` + mirrors the harness's JSONL session store to a ``flyte.Checkpoint``, and on the + retry it restores that transcript and prompts the same session id, so the + harness continues the prior conversation instead of starting over (the session + id is derived from the task's action, so every attempt points at one session); +- completed tool calls are cache hits — each tool is a durable Flyte child action + with ``cache="auto"``, so it isn't re-executed on the retry. + +Backend only: per-attempt numbers and the previous attempt's checkpoint are provided +by the platform per attempt — that is where resume is exercised. In ``local`` mode the +crash is skipped and the example just runs once. + +Run: flyte run deepseek_crash_resume.py resilient_agent --question "What's the weather and population of Paris?" + (add `--local` right after `run` to execute locally instead of on the backend) +""" + +import os + +import flyte + +from flyteplugins.agents.deepseek import run_agent, tool + +env = flyte.TaskEnvironment( + "deepseek-crash-resume", + resources=flyte.Resources(cpu=1), + secrets=[flyte.Secret(key="deepseek_api_key", as_env_var="DEEPSEEK_API_KEY")], + image=flyte.Image.from_debian_base(name="deepseek-crash-resume").with_local_v2_plugins( + ["flyteplugins-agents-core", "flyteplugins-agents-deepseek"] + ), +) + + +@tool +@env.task(cache="auto", retries=3) +async def get_weather(city: str) -> str: + """Get the current weather for a city.""" + print(f" 🛠 get_weather EXECUTED for {city} (cache MISS)", flush=True) + return f"sunny, 22°C in {city}" + + +@tool +@env.task(cache="auto", retries=3) +async def get_population(city: str) -> int: + """Get the population of a city.""" + print(f" 🛠 get_population EXECUTED for {city} (cache MISS)", flush=True) + return {"San Francisco": 808988, "Paris": 2102650, "Tokyo": 13929286}.get(city, 1_000_000) + + +@env.task(report=True, retries=2) +async def resilient_agent(question: str) -> str: + attempt = flyte.ctx().attempt_number if flyte.ctx() else 0 + print(f"▶ resilient_agent attempt {attempt}", flush=True) + + answer = await run_agent( + question, + tools=[get_weather, get_population], + instructions="You are a concise city-facts assistant. Use the provided tools to answer.", + model="deepseek-v4-flash", + durable=True, # default — mirror the session to a checkpoint and resume on retry + ) + + # Simulate a worker crash after the agent did real work, but only on the first + # attempt on a backend. ``FLYTE_ATTEMPT_NUMBER`` is only set per attempt on a + # backend, so local runs skip the crash and just complete. + on_backend = os.environ.get("FLYTE_ATTEMPT_NUMBER") is not None + if on_backend and attempt == 0: + raise RuntimeError("💥 simulated worker crash (first attempt only)") + + print("✅ completed on retry — tools were cache hits, conversation resumed", flush=True) + return answer + + +if __name__ == "__main__": + flyte.init_from_config() + run = flyte.run(resilient_agent, question="What's the weather and population of Paris?") + print(f"View at: {run.url}") + run.wait() + print(f"Result: {run.outputs()}") diff --git a/plugins/agents/deepseek/examples/deepseek_custom_agent.py b/plugins/agents/deepseek/examples/deepseek_custom_agent.py new file mode 100644 index 000000000..c6caa0339 --- /dev/null +++ b/plugins/agents/deepseek/examples/deepseek_custom_agent.py @@ -0,0 +1,71 @@ +"""Bring your own harness configuration — SDK-native config, Flyte underneath. + +The adapter is a thin layer, not a wrapper that hides the SDK. When you need the +harness configured your way — a custom Cordis plugin composition, a proxied +endpoint, tighter timeouts — build a ``DeepSeekHarnessConfig`` exactly as you +would outside Flyte and pass it as ``config=``. + +The adapter layers only what it must on top: + +- ``cwd`` — the workspace, because that is where the tool shims are published; +- ``session_root`` — because that is what durable resume and cross-run memory + mirror. + +Everything else on the config is yours, and ``model`` / ``provider`` / +``max_tokens`` / ``**kwargs`` on ``run_agent`` override fields on top of it. + +Run: flyte run deepseek_custom_agent.py configured_agent --question "What's the population of Tokyo?" + (add `--local` right after `run` to execute locally instead of on the backend) +""" + +import flyte +from deepseek_harness import DeepSeekHarnessConfig + +from flyteplugins.agents.deepseek import run_agent, tool + +env = flyte.TaskEnvironment( + "deepseek-custom-agent", + resources=flyte.Resources(cpu=1), + secrets=[flyte.Secret(key="deepseek_api_key", as_env_var="DEEPSEEK_API_KEY")], + image=flyte.Image.from_debian_base(name="deepseek-custom-agent").with_local_v2_plugins( + ["flyteplugins-agents-core", "flyteplugins-agents-deepseek"] + ), +) + + +@tool +@env.task(cache="auto", retries=3) +async def get_population(city: str) -> int: + """Get the population of a city.""" + return {"San Francisco": 808988, "Paris": 2102650, "Tokyo": 13929286}.get(city, 1_000_000) + + +@env.task(report=True, retries=3) +async def configured_agent(question: str) -> str: + """Drive the agent from a hand-built SDK config.""" + # Native SDK configuration — the same object you would use standalone. Point + # ``cordis`` at your own plugin composition to change the harness's own tools, + # persistence or provider routes (keep the JSON-RPC server entry in it). + config = DeepSeekHarnessConfig( + provider="deepseek-official", + model="deepseek-v4-flash", + max_tokens=49_152, + request_timeout_seconds=300.0, + # cordis="/opt/harness/my.cordis.yml", + # base_url="https://my-proxy.internal/v1", + ) + + return await run_agent( + question, + tools=[get_population], + instructions="You are a concise city-facts assistant. Use the provided tools to answer.", + config=config, + ) + + +if __name__ == "__main__": + flyte.init_from_config() + run = flyte.run(configured_agent, question="What's the population of Tokyo?") + print(f"View at: {run.url}") + run.wait() + print(f"Result: {run.outputs()}") diff --git a/plugins/agents/deepseek/examples/deepseek_durable_agent.py b/plugins/agents/deepseek/examples/deepseek_durable_agent.py new file mode 100644 index 000000000..9f14939f9 --- /dev/null +++ b/plugins/agents/deepseek/examples/deepseek_durable_agent.py @@ -0,0 +1,82 @@ +"""Run a DeepSeek Harness agent durably on Flyte (single agent). + +The "bring your own agent SDK" path: you keep writing DeepSeek Harness code, and +Flyte is the runtime underneath. + +- Each tool is a Flyte task — it runs as a durable child action (its own + container/resources, with retries and caching) when the agent calls it. +- The agent timeline (assistant turns, tool calls and their results) is rendered + into the task report because the task is created with ``report=True``. +- ``run_agent`` is async: ``await run_agent(...)`` from async tasks, while sync + tasks call ``run_agent_sync(...)`` (see ``city_agent_sync``). + +Because the harness has no tool-registration channel of its own, each tool is +published into the harness workspace as an executable command and described to +the model in its prompt; running it calls straight back into this task. See the +adapter README for the mechanism. + +Run: flyte run deepseek_durable_agent.py city_agent --question "What's the weather of Paris?" + (or drive the sync variant: flyte run deepseek_durable_agent.py city_agent_sync --question "...") + (add `--local` right after `run` to execute locally instead of on the backend) +""" + +import flyte + +from flyteplugins.agents.deepseek import run_agent, run_agent_sync, tool + +env = flyte.TaskEnvironment( + "deepseek-durable-agent", + resources=flyte.Resources(cpu=1), + secrets=[flyte.Secret(key="deepseek_api_key", as_env_var="DEEPSEEK_API_KEY")], + image=flyte.Image.from_debian_base(name="deepseek-durable-agent").with_local_v2_plugins( + ["flyteplugins-agents-core", "flyteplugins-agents-deepseek"] + ), +) + + +# Stack @tool on top of @env.task: each is both a tool the agent can +# call and a normal, durable, cached Flyte task. +@tool +@env.task(cache="auto", retries=3) +async def get_weather(city: str) -> str: + """Get the current weather for a city.""" + return f"The weather in {city} is sunny, 22°C." + + +@tool +@env.task(cache="auto", retries=3) +async def get_population(city: str) -> int: + """Get the population of a city.""" + return {"San Francisco": 808988, "Paris": 2102650, "Tokyo": 13929286}.get(city, 1_000_000) + + +@env.task(report=True, retries=3) +async def city_agent(question: str) -> str: + """The durable parent: the DeepSeek Harness agent loop runs here, on Flyte.""" + return await run_agent( + question, + tools=[get_weather, get_population], + instructions="You are a concise city-facts assistant. Use the provided tools to answer.", + model="deepseek-v4-flash", + ) + + +# ``run_agent`` is async: async tasks await ``run_agent(...)`` (above), +# while sync tasks call ``run_agent_sync(...)``. +@env.task(report=True, retries=3) +def city_agent_sync(question: str) -> str: + """The same agent, driven from a sync task via the sync call form.""" + return run_agent_sync( + question, + tools=[get_weather, get_population], + instructions="You are a concise city-facts assistant. Use the provided tools to answer.", + model="deepseek-v4-flash", + ) + + +if __name__ == "__main__": + flyte.init_from_config() + run = flyte.run(city_agent, question="What's the weather of Paris?") + print(f"View at: {run.url}") + run.wait() + print(f"Result: {run.outputs()}") diff --git a/plugins/agents/deepseek/examples/deepseek_memory.py b/plugins/agents/deepseek/examples/deepseek_memory.py new file mode 100644 index 000000000..9117c6718 --- /dev/null +++ b/plugins/agents/deepseek/examples/deepseek_memory.py @@ -0,0 +1,65 @@ +"""Cross-run agent memory on Flyte — a DeepSeek agent remembers across separate runs. + +``run_agent(..., memory_key=...)`` persists the harness's session transcript to a +durable, keyed ``MemoryStore`` and restores it on the next run with the same key — +so the agent remembers across workers and restarts. (The harness writes its +sessions as JSONL under an ephemeral per-pod ``session_root``; Flyte makes that +durable and addressable by thread, then prompts the same session id so the +harness simply continues the conversation.) + +Two separate runs share one ``memory_key``: it learns a fact in run 1 and recalls +it in run 2. + +Memory is keyed under the active org/project/domain, so run with a configured +context (``flyte.init_from_config()`` / a backend). ``memory_key`` is a single +segment (a user/thread id). + +Run: flyte run deepseek_memory.py chat --message "Hi! My name is Alice and I love hiking." --memory_key user-alice + flyte run deepseek_memory.py chat --message "What's my name and what do I like?" --memory_key user-alice + (add `--local` after `run` to run locally; the shared `--memory_key` ties the two runs) +""" + +import flyte + +from flyteplugins.agents.deepseek import run_agent + +# The `deepseek-harness-sdk` wheel pulls in the bundled harness runtime, so the +# image only needs the adapter — installed here from locally-built wheels. +env = flyte.TaskEnvironment( + "deepseek-memory", + resources=flyte.Resources(cpu=1), + secrets=[flyte.Secret(key="deepseek_api_key", as_env_var="DEEPSEEK_API_KEY")], + image=flyte.Image.from_debian_base(name="deepseek-memory").with_local_v2_plugins( + ["flyteplugins-agents-core", "flyteplugins-agents-deepseek"] + ), +) + + +@env.task(report=True, retries=3) +async def chat(message: str, memory_key: str) -> str: + """One turn of a memory-backed conversation, keyed by ``memory_key``. + + Because ``memory_key`` is stable across runs, the agent resumes the prior + conversation every time it is called with the same key. + """ + return await run_agent( + message, + instructions="You are a friendly assistant. Use the conversation history to stay consistent.", + model="deepseek-v4-flash", + memory_key=memory_key, + ) + + +if __name__ == "__main__": + flyte.init_from_config() + + # Run 1: the agent learns a fact. + r1 = flyte.run(chat, message="Hi! My name is Alice and I love hiking.", memory_key="user-alice") + r1.wait() + print(f"run 1: {r1.outputs()}") + + # Run 2 — a SEPARATE run with the same memory_key: the agent recalls it. + r2 = flyte.run(chat, message="What's my name and what do I like?", memory_key="user-alice") + print(f"View at: {r2.url}") + r2.wait() + print(f"run 2 (recall): {r2.outputs()}") diff --git a/plugins/agents/deepseek/examples/deepseek_multi_agent.py b/plugins/agents/deepseek/examples/deepseek_multi_agent.py new file mode 100644 index 000000000..ddef3fef3 --- /dev/null +++ b/plugins/agents/deepseek/examples/deepseek_multi_agent.py @@ -0,0 +1,119 @@ +"""Multi-agent research pipeline — Flyte orchestrating many DeepSeek Harness agents. + +The "Flyte as the orchestrator harness" story for DeepSeek Harness. Instead of one +agent, a small team is composed with ordinary Flyte control flow, and each agent +run is its own durable, observable, cached Flyte action: + + plan --> research (parallel, one per subtopic) --> synthesize + +- ``plan`` runs a planner agent that breaks the topic into subtopics. +- ``research`` runs a researcher agent per subtopic, fanned out in parallel + (``asyncio.gather``); each is a separate durable child action with its own + ``search_web`` tool calls and report. +- ``synthesize`` runs an editor agent that combines the findings. + +Every agent is a first-class Flyte node — independently retried, cached, +resource-sized and observable — and the fan-out is real distributed parallelism, +not just asyncio in one process. Each ``run_agent`` gets its own harness runtime +subprocess and its own workspace, so the agents cannot tread on each other. + +Run: flyte run deepseek_multi_agent.py research_pipeline --topic "The state of electric-vehicle batteries in 2025" + (add `--local` right after `run` to execute locally instead of on the backend) +""" + +import asyncio + +import flyte + +from flyteplugins.agents.deepseek import run_agent, tool + +env = flyte.TaskEnvironment( + "deepseek-research", + resources=flyte.Resources(cpu=1), + secrets=[flyte.Secret(key="deepseek_api_key", as_env_var="DEEPSEEK_API_KEY")], + image=flyte.Image.from_debian_base(name="deepseek-research").with_local_v2_plugins( + ["flyteplugins-agents-core", "flyteplugins-agents-deepseek"] + ), +) + +# A real deployment would call a search API here. Tools are Flyte tasks, so this +# could just as well be a GPU retrieval task, a warehouse query, or a browser +# session — each sized and cached independently. +_CORPUS = { + "battery": "Solid-state cells reached 500 Wh/kg in 2025 pilot lines; cycle life is the open problem.", + "charging": "Public DC fast-charger count doubled YoY; grid interconnect queues are the bottleneck.", + "cost": "Pack prices fell below $80/kWh in 2025, crossing the oft-cited parity threshold.", +} + + +@tool +@env.task(cache="auto", retries=3) +async def search_web(query: str) -> str: + """Search the web for a query and return the most relevant snippet.""" + for key, snippet in _CORPUS.items(): + if key in query.lower(): + return snippet + return "No strong match; rely on general knowledge and state assumptions." + + +@env.task(retries=3) +async def plan(topic: str) -> list[str]: + """Planner agent: decompose a topic into focused research subtopics.""" + text = await run_agent( + f"Break the topic '{topic}' into exactly 3 focused, distinct research subtopics.", + instructions="Reply with only a comma-separated list of 3 short subtopics. No numbering, no prose.", + model="deepseek-v4-flash", + ) + # Forgiving parse: accept commas or newlines, strip bullets/numbering. + raw = [part.strip(" -•0123456789.").strip() for chunk in text.splitlines() for part in chunk.split(",")] + subtopics = [s for s in raw if s] + return subtopics[:3] or [topic] + + +@env.task(report=True, retries=3) +async def research(subtopic: str) -> str: + """Researcher agent: investigate one subtopic using the search tool.""" + return await run_agent( + f"Research this subtopic and summarize the key findings as 3 concise bullet points:\n{subtopic}", + tools=[search_web], + # A clear stop condition matters: this toy ``search_web`` returns the same snippet + # for related queries, so an open-ended "keep searching" prompt can make a model spin. + instructions=( + "You are a rigorous research assistant. Call search_web exactly once for this " + "subtopic, then STOP searching and write exactly 3 concise bullet points from the " + "result (rely on general knowledge if the search was unhelpful). Do not search again." + ), + model="deepseek-v4-flash", + ) + + +@env.task(report=True, retries=3) +async def synthesize(topic: str, findings: list[str]) -> str: + """Editor agent: synthesize the per-subtopic findings into a briefing.""" + notes = "\n\n".join(f"- {f}" for f in findings) + return await run_agent( + f"Topic: {topic}\n\nResearch notes:\n{notes}\n\nWrite a tight one-paragraph executive briefing.", + instructions="You are a sharp editor. Synthesize the notes faithfully; do not invent facts.", + model="deepseek-v4-flash", + ) + + +@env.task(report=True, retries=3) +async def research_pipeline(topic: str) -> str: + """Orchestrate the agent team: plan → parallel research → synthesize.""" + subtopics = await plan(topic) + + # Fan out one researcher agent per subtopic. Each is a durable child action; + # grouping keeps them tidy in the Flyte UI. + with flyte.group("parallel-research"): + findings = await asyncio.gather(*(research(subtopic) for subtopic in subtopics)) + + return await synthesize(topic, list(findings)) + + +if __name__ == "__main__": + flyte.init_from_config() + run = flyte.run(research_pipeline, topic="The state of electric-vehicle batteries in 2025") + print(f"View at: {run.url}") + run.wait() + print(f"Briefing:\n{run.outputs()}") diff --git a/plugins/agents/deepseek/examples/deepseek_workspace_agent.py b/plugins/agents/deepseek/examples/deepseek_workspace_agent.py new file mode 100644 index 000000000..c1051bd13 --- /dev/null +++ b/plugins/agents/deepseek/examples/deepseek_workspace_agent.py @@ -0,0 +1,130 @@ +"""Give the agent a real workspace — DeepSeek Harness editing files on Flyte. + +This is what the harness is built for. Its own tools (local bash, the string +editor) act on a working directory, so pointing ``workspace=`` at a real +directory turns the agent loose on actual files instead of a scratch temp dir. + +The pipeline is ordinary Flyte: + + seed_repo --> fix_the_code (agent) --> the patched directory + +- ``seed_repo`` produces a ``flyte.io.Dir`` — here a tiny project with a failing + function, but just as easily a checkout, a dbt project, or a data drop. +- ``fix_the_code`` downloads it, hands the local path to ``run_agent`` as the + workspace, and lets the agent read/edit/run whatever it needs. Its Flyte-task + tool (``run_tests``) is published into that same workspace, so the agent can + verify its own work — and each verification is a durable, cached child action. +- The edited directory is uploaded back as the task's output, so the change is a + first-class, versioned artifact rather than a side effect in a dead container. + +Everything the workspace-less examples get still applies: retries resume the +conversation, tool calls are durable child actions, and the timeline lands in the +task report. + +Run: flyte run deepseek_workspace_agent.py fix_pipeline + (add `--local` right after `run` to execute locally instead of on the backend) +""" + +import asyncio +import pathlib +import subprocess +import tempfile + +import flyte +from flyte.io import Dir + +from flyteplugins.agents.deepseek import run_agent, tool + +env = flyte.TaskEnvironment( + "deepseek-workspace", + resources=flyte.Resources(cpu=2, memory="2Gi"), + secrets=[flyte.Secret(key="deepseek_api_key", as_env_var="DEEPSEEK_API_KEY")], + image=flyte.Image.from_debian_base(name="deepseek-workspace").with_local_v2_plugins( + ["flyteplugins-agents-core", "flyteplugins-agents-deepseek"] + ), +) + +# A deliberately broken implementation: ``median`` doesn't average the two middle +# values on even-length input, so the second test fails. +_BROKEN_SOURCE = """\ +# Return the median of a list of numbers. +def median(values): + ordered = sorted(values) + return ordered[len(ordered) // 2] +""" + +_TESTS = """\ +from stats import median + + +def test_odd_length(): + assert median([3, 1, 2]) == 2 + + +def test_even_length(): + assert median([4, 1, 3, 2]) == 2.5 +""" + + +@env.task +async def seed_repo() -> Dir: + """Produce the little project the agent will be asked to fix.""" + workdir = pathlib.Path(tempfile.mkdtemp(prefix="repo-")) + (workdir / "stats.py").write_text(_BROKEN_SOURCE) + (workdir / "test_stats.py").write_text(_TESTS) + return await Dir.from_local(str(workdir)) + + +@tool +@env.task(retries=2) +async def run_tests(directory: str) -> str: + """Run the test suite in a directory and return the pytest output.""" + done = await asyncio.to_thread( + subprocess.run, + ["python", "-m", "pytest", "-q", directory], + capture_output=True, + text=True, + check=False, + timeout=300, + ) + return f"exit code {done.returncode}\n{done.stdout}\n{done.stderr}".strip() + + +@env.task(report=True, retries=3) +async def fix_the_code(repo: Dir) -> Dir: + """Agent task: let the harness read, edit and verify real files.""" + local_path = await repo.download() + + await run_agent( + "The test suite in this directory is failing. Read the code, find the bug in " + "stats.py, fix it, and use the run_tests tool to confirm every test passes. " + "Reply with a one-line summary of the fix.", + tools=[run_tests], + instructions=( + "You are a careful software engineer. Make the smallest correct change, and " + "verify with run_tests before you finish." + ), + model="deepseek-v4-flash", + # The harness's bash and editor act here; the run_tests shim is published + # into this same directory, so the agent can call it like any command. + workspace=local_path, + ) + + # The agent edited the files in place — hand the patched directory downstream. + return await Dir.from_local(str(local_path)) + + +@env.task(report=True, retries=3) +async def fix_pipeline() -> str: + """Seed a broken project, let the agent fix it, then verify independently.""" + patched = await fix_the_code(await seed_repo()) + # Verify with our own task rather than trusting the agent's account of itself. + return await run_tests(await patched.download()) + + +if __name__ == "__main__": + flyte.init_from_config() + run = flyte.run(fix_pipeline) + print(f"View at: {run.url}") + run.wait() + print(f"Verification:\n{run.outputs()}") diff --git a/plugins/agents/deepseek/pyproject.toml b/plugins/agents/deepseek/pyproject.toml new file mode 100644 index 000000000..ba212ecf0 --- /dev/null +++ b/plugins/agents/deepseek/pyproject.toml @@ -0,0 +1,63 @@ +[project] +name = "flyteplugins-agents-deepseek" +dynamic = ["version"] +description = "Run DeepSeek Harness agents on Flyte." +readme = "README.md" +authors = [{ name = "Niels Bantilan", email = "niels@union.ai" }] +requires-python = ">=3.10" +dependencies = [ + "flyteplugins-agents-core", + "deepseek-harness-sdk>=0.1.0rc7", +] + +[build-system] +requires = ["setuptools", "setuptools_scm"] +build-backend = "setuptools.build_meta" + +[dependency-groups] +dev = [ + "pytest>=8.3.5", + "pytest-asyncio>=0.26.0", +] + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.packages.find] +where = ["src"] +include = ["flyteplugins*"] + +[tool.setuptools_scm] +root = "../../.." + +[tool.pytest.ini_options] +norecursedirs = [] +log_cli = true +log_cli_level = 20 +markers = [] +asyncio_default_fixture_loop_scope = "function" + +[tool.coverage.run] +branch = true + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = ["E", "W", "F", "I", "PLW", "YTT", "ASYNC", "C4", "T10", "EXE", "ISC", "LOG", "PIE", "Q", "RSE", "FLY", "PGH", "PLC", "PLE", "PLW", "FURB", "RUF"] +ignore = ["PGH003", "PLC0415"] + +[tool.ruff.lint.per-file-ignores] +"examples/*" = ["E402"] +# Tests assert on the local filesystem (workspace/session-root setup and cleanup) +# from async test bodies; blocking there is intended and harmless. +"tests/*" = ["ASYNC240"] + +[tool.uv] +# `deepseek-harness-sdk` only publishes pre-releases so far (0.1.0rcN), and its +# pinned `deepseek-harness-runtime-bin` dependency is a pre-release too. +prerelease = "allow" + +[tool.uv.sources] +flyte = { path = "../../..", editable = true } +flyteplugins-agents-core = { path = "../core", editable = true } diff --git a/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/__init__.py b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/__init__.py new file mode 100644 index 000000000..35d602fb5 --- /dev/null +++ b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/__init__.py @@ -0,0 +1,29 @@ +"""DeepSeek Harness adapter for Flyte. + +Bring your own `deepseek-harness-sdk` agent and run it durably on Flyte. The +harness owns the agent loop (it drives the model inside its own runtime +subprocess); Flyte is the durable runtime underneath — retries / self-healing, +per-tool containerized execution, cross-run memory, and the agent timeline in +the task report. + +- `flyteplugins.agents.deepseek.tool` — turn an `@env.task` into a harness tool. +- `flyteplugins.agents.deepseek.run_agent` — run the agent loop inside your task + and return the answer. + +Tools work differently here than in the client-side SDKs. The harness has no +tool-registration message: its tool surface is whatever its Cordis composition +provides, inside the runtime subprocess. So a Flyte-task tool is published into +the harness workspace as an executable shim that calls back into this process, +where the real task runs as a durable child action. The mechanism is in +`._bridge`; the call shape is the shared one every adapter presents. + +The `deepseek-harness-sdk` wheel pulls in the bundled `deepseek-harness-runtime-bin` +runtime (no separate Node.js install needed); set `DEEPSEEK_API_KEY` in the +environment, and `DEEPSEEK_BASE_URL` if you route through a proxy. +""" + +from ._bridge import TOOLS_DIRNAME, ToolBridge +from ._run import run_agent, run_agent_sync +from ._tools import HarnessTool, tool + +__all__ = ["TOOLS_DIRNAME", "HarnessTool", "ToolBridge", "run_agent", "run_agent_sync", "tool"] diff --git a/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_bridge.py b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_bridge.py new file mode 100644 index 000000000..2cb38da64 --- /dev/null +++ b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_bridge.py @@ -0,0 +1,251 @@ +"""The tool bridge — let the harness runtime call Flyte tasks as durable actions. + +The harness runs the model loop in its own subprocess and only ever calls tools +that its Cordis composition provides. The one such tool every composition ships +is local bash, scoped to the workspace directory the adapter chooses. So the +bridge meets it there: + +1. each `HarnessTool` is published into `/.flyte_tools/` as an + executable shim (stdlib-only Python, run under this process's own + interpreter, so it needs nothing installed in the harness runtime); +2. this process listens on a Unix domain socket next to the run; +3. the model runs `.flyte_tools/get_weather '{"city": "Paris"}'` via bash, the + shim forwards the JSON arguments over the socket, and the bridge awaits + `task.aio(...)` — a durable Flyte child action — before writing the result + back for the shim to print. + +The shim speaks one newline-delimited JSON request/response per connection, so +concurrent tool calls are just concurrent connections. + +The socket lives in a private temp directory rather than the workspace: the +model can read and write the workspace, and a socket it can reach directly is +one more thing to reason about. The workspace only ever holds the shims. +""" + +from __future__ import annotations + +import asyncio +import json +import os +import shutil +import stat +import sys +import tempfile +import typing +from pathlib import Path + +from flyte._logging import logger +from flyteplugins.agents.core import ReportTimeline, abbrev + +if typing.TYPE_CHECKING: + from ._tools import HarnessTool + +# Directory (relative to the harness workspace) holding the published shims. +TOOLS_DIRNAME = ".flyte_tools" + +# Written verbatim into each shim; ``SOCKET_PATH`` / ``TOOL_NAME`` are filled in +# per tool. Stdlib only, so any Python 3 interpreter can run it. +_SHIM = '''\ +#!{interpreter} +"""Flyte tool shim for {tool_name!r} — forwards to the Flyte task over a socket.""" +import json +import socket +import sys + +SOCKET_PATH = {socket_path!r} +TOOL_NAME = {tool_name!r} + + +def main() -> int: + raw = (sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read()).strip() or "{{}}" + try: + args = json.loads(raw) + except ValueError as exc: + print(f"error: arguments must be a JSON object ({{exc}}): {{raw}}", file=sys.stderr) + return 2 + if not isinstance(args, dict): + print("error: arguments must be a JSON object, e.g. '{{\\"city\\": \\"Paris\\"}}'", file=sys.stderr) + return 2 + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + sock.connect(SOCKET_PATH) + except OSError as exc: + print(f"error: the Flyte tool bridge is not reachable: {{exc}}", file=sys.stderr) + return 3 + try: + stream = sock.makefile("rwb") + stream.write((json.dumps({{"tool": TOOL_NAME, "args": args}}) + "\\n").encode("utf-8")) + stream.flush() + line = stream.readline() + finally: + sock.close() + + if not line: + print("error: the Flyte tool bridge closed without responding", file=sys.stderr) + return 3 + response = json.loads(line.decode("utf-8")) + if response.get("ok"): + print(response.get("result", "")) + return 0 + print(f"error: {{response.get('error') or 'tool call failed'}}", file=sys.stderr) + return 1 + + +sys.exit(main()) +''' + + +class ToolBridge: + """Publish `HarnessTool`s into a workspace and serve their callbacks. + + Use as an async context manager around the harness run; on exit the socket + and its temp directory are removed (the published shims are left in the + workspace, which the adapter owns and cleans up separately). + """ + + def __init__(self, tools: typing.Sequence["HarnessTool"], *, timeline: ReportTimeline | None = None) -> None: + self._tools = {t.name: t for t in tools} + self._timeline = timeline + self._server: asyncio.AbstractServer | None = None + self._sockdir: str | None = None + self._socket_path: str | None = None + self._tools_dir: Path | None = None + + @property + def socket_path(self) -> str | None: + return self._socket_path + + @property + def tools_dir(self) -> Path | None: + return self._tools_dir + + async def start(self, workspace: Path) -> None: + """Start the callback server and publish a shim per tool into `workspace`.""" + if not self._tools: + return + self._sockdir = tempfile.mkdtemp(prefix="flyte-dsh-") + self._socket_path = os.path.join(self._sockdir, "tools.sock") + self._server = await asyncio.start_unix_server(self._handle, path=self._socket_path) + self._publish(workspace) + + async def stop(self) -> None: + """Close the server and remove the socket directory. Never raises.""" + if self._server is not None: + self._server.close() + try: + await self._server.wait_closed() + except Exception: # pragma: no cover - already torn down + logger.debug("DeepSeek tool bridge: server close raced with shutdown", exc_info=True) + self._server = None + if self._sockdir: + shutil.rmtree(self._sockdir, ignore_errors=True) + self._sockdir = None + self._socket_path = None + + async def __aenter__(self) -> "ToolBridge": # pragma: no cover - exercised via run_agent + return self + + async def __aexit__(self, _exc_type, _exc, _tb) -> None: # pragma: no cover - exercised via run_agent + await self.stop() + + def _publish(self, workspace: Path) -> None: + """Write one executable shim per tool into `/.flyte_tools/`.""" + tools_dir = workspace / TOOLS_DIRNAME + tools_dir.mkdir(parents=True, exist_ok=True) + for name in self._tools: + shim = tools_dir / name + shim.write_text( + _SHIM.format(interpreter=sys.executable, socket_path=self._socket_path, tool_name=name), + encoding="utf-8", + ) + shim.chmod(shim.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + self._tools_dir = tools_dir + + def instructions(self) -> str: + """The tool manual handed to the model, or `""` when there are no tools. + + The harness has no tool-declaration channel, so the contract is stated in + prose: what exists, how to call it, and what comes back. + """ + if not self._tools: + return "" + lines = [ + "## Flyte tools", + "", + ( + "The following tools are available as executable commands in " + f"`{TOOLS_DIRNAME}/` under your working directory. Run one with bash, passing its" + ), + "arguments as a single JSON object argument:", + "", + f" {TOOLS_DIRNAME}/ ''", + "", + "The tool prints its result to stdout and exits 0; on failure it prints the reason", + "to stderr and exits non-zero. Prefer these tools over writing your own code for the", + "same job — each one runs as a tracked, retried step of this workflow.", + "", + "Available tools:", + "", + ] + for name, harness_tool in self._tools.items(): + lines.append(f"- `{harness_tool.usage()}`") + lines.append(f" example: `{TOOLS_DIRNAME}/{name} '{_example_args(harness_tool)}'`") + return "\n".join(lines) + + async def _handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None: + """Serve one shim callback: decode, dispatch to the task, write the result.""" + try: + line = await reader.readline() + if not line: + return + response = await self._dispatch(json.loads(line.decode("utf-8"))) + writer.write((json.dumps(response, default=str) + "\n").encode("utf-8")) + await writer.drain() + except Exception as exc: # pragma: no cover - transport-level failure + logger.warning("DeepSeek tool bridge: failed to serve a tool call: %s", exc) + finally: + writer.close() + + async def _dispatch(self, request: dict[str, typing.Any]) -> dict[str, typing.Any]: + """Run the requested tool, returning the shim's `{ok, result|error}` reply.""" + name = request.get("tool") + args = request.get("args") or {} + harness_tool = self._tools.get(name) + if harness_tool is None: + return {"ok": False, "error": f"unknown tool {name!r}; available: {', '.join(self._tools)}"} + + if self._timeline is not None: + self._timeline.row(icon="🛠️", label=name, meta="tool", detail=abbrev(args, 160)) + try: + result = await harness_tool.invoke(args) + except Exception as exc: + # A failed tool is reported to the model, not raised: the agent gets to + # see the error and decide what to do, exactly as with a native tool. + if self._timeline is not None: + self._timeline.row(icon="❌", label=name, meta="tool error", detail=abbrev(exc, 160), error="error") + return {"ok": False, "error": f"{type(exc).__name__}: {exc}"} + if self._timeline is not None: + self._timeline.row(icon="🔧", label=name, meta="tool result", detail=abbrev(result, 160)) + return {"ok": True, "result": result} + + +def _example_args(harness_tool: "HarnessTool") -> str: + """A filled-in example argument object, so the model sees the exact shape.""" + placeholders = { + "string": "...", + "integer": 0, + "number": 0, + "boolean": True, + "array": [], + "object": {}, + } + properties = harness_tool.schema.get("properties") or {} + example = {} + for key, spec in properties.items(): + declared = spec.get("type") if isinstance(spec, dict) else None + if isinstance(spec, dict) and spec.get("enum"): + example[key] = spec["enum"][0] + else: + example[key] = placeholders.get(declared if isinstance(declared, str) else "", "...") + return json.dumps(example) diff --git a/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_durable.py b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_durable.py new file mode 100644 index 000000000..7c630fe78 --- /dev/null +++ b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_durable.py @@ -0,0 +1,164 @@ +"""Durable harness sessions — make `durable=True` real via the SDK's session store. + +DeepSeek Harness runs the model loop inside its runtime subprocess, so there is +no in-process model-call seam to wrap in `flyte.trace` for per-turn replay (the +same constraint the Claude adapter has). What the harness does own is session +persistence: every session is written as JSONL under `session_root` +(`DSH_SESSION_ROOT`), and prompting an existing `session_id` continues that +session rather than starting a new one. + +So durability here is session resume, backed by `flyte.Checkpoint` — the native, +retry-surviving durable prefix the runtime hands each task (`save` writes this +attempt's blob, `load` restores the previous attempt's): + +- the session id is derived deterministically from the task's `ActionID`, so + every retry of the same action targets the same session; +- before the harness starts, a previous attempt's session files are restored + into `session_root`; after each run they are snapshotted back. + +A crashed attempt therefore resumes its conversation instead of restarting it — +without us owning the loop. Tool durability, retries and caching apply either way. +""" + +from __future__ import annotations + +import json +import pathlib +import typing +import uuid + +from flyte._logging import logger + +# Fixed namespace so derived session ids are stable across processes and retries. +_SESSION_NS = uuid.UUID("6f2d9c14-8b7e-5a3f-9d21-4c8e5b7a0f13") +_PAYLOAD = "payload" + +# A session transcript is JSONL; anything larger than this is left out of the +# snapshot rather than pushed through the checkpoint blob. +_MAX_FILE_BYTES = 32 * 1024 * 1024 + + +def deterministic_session_id(task_context: typing.Any) -> str: + """A stable harness session id for the current action (same across retries). + + Uses `task_action` when present (it stays pinned to the real running task even + inside a `@trace` pseudo-action), falling back to `action`. + """ + action = getattr(task_context, "task_action", None) or task_context.action + seed = f"{action.run_name}/{action.name}" + return f"flyte-{uuid.uuid5(_SESSION_NS, seed).hex}" + + +def snapshot(session_root: pathlib.Path) -> dict[str, str]: + """Read a `session_root` tree into a `{relative path: text}` mapping. + + Best-effort: unreadable, binary or oversized entries are skipped, so a + snapshot never fails the run that produced it. + """ + archive: dict[str, str] = {} + if not session_root.is_dir(): + return archive + for path in sorted(session_root.rglob("*")): + if not path.is_file(): + continue + try: + if path.stat().st_size > _MAX_FILE_BYTES: + logger.warning("DeepSeek session file %s is too large to persist; skipping.", path.name) + continue + archive[str(path.relative_to(session_root))] = path.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError): + logger.debug("DeepSeek session file %s could not be read for persistence; skipping.", path, exc_info=True) + return archive + + +def restore(session_root: pathlib.Path, archive: typing.Mapping[str, str]) -> bool: + """Materialize an archive back into `session_root`; returns whether anything was written.""" + if not archive: + return False + written = False + for relative, text in archive.items(): + path = session_root / relative + try: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + written = True + except OSError: # pragma: no cover - unwritable session root + logger.debug("Could not restore DeepSeek session file %s.", relative, exc_info=True) + return written + + +def _read_payload(local: typing.Any) -> dict | None: + """Parse the checkpoint blob written by a previous attempt, or `None`. + + Kept sync (local, already-downloaded file IO) so the async wiring stays free of + blocking `pathlib` calls. + """ + payload = pathlib.Path(local) + if payload.is_dir(): + payload = payload / _PAYLOAD + if not payload.is_file(): + return None + try: + return json.loads(payload.read_text()) + except (ValueError, OSError): + return None + + +class CheckpointSession: + """A harness session whose `session_root` is mirrored to a `flyte.Checkpoint`. + + `seed` restores the previous attempt's transcript before the harness starts; + `persist` snapshots it back after each run. `resumed` reports whether this + attempt actually picked up a prior conversation. + """ + + def __init__(self, checkpoint: typing.Any, session_id: str) -> None: + self._ckpt = checkpoint + self.session_id = session_id + self.resumed = False + + async def seed(self, session_root: pathlib.Path) -> bool: + """Restore a prior attempt's session files into `session_root`.""" + local = await self._ckpt.load() + if local is None: + return False + archive = _read_payload(local) + if not archive: + return False + self.resumed = restore(session_root, archive) + return self.resumed + + async def persist(self, session_root: pathlib.Path) -> None: + """Snapshot `session_root` into the checkpoint. Never raises.""" + try: + archive = snapshot(session_root) + if archive: + await self._ckpt.save(json.dumps(archive).encode("utf-8")) + except Exception: # pragma: no cover - durability must never break the run + logger.warning("Could not persist the DeepSeek session checkpoint; continuing.") + + +async def wire_durable_session(session_root: pathlib.Path, *, durable: bool) -> CheckpointSession | None: + """Build a checkpoint-backed session for this action, seeded from any prior attempt. + + Returns `None` when durability is off or unavailable (no task context, no + checkpoint — e.g. a local run), in which case the caller runs with a fresh + session. Never raises: durability is best-effort and must not break a run. + """ + if not durable: + return None + try: + import flyte + + task_context = flyte.ctx() + if task_context is None: + return None + checkpoint = task_context.checkpoint + if checkpoint is None: + return None + session = CheckpointSession(checkpoint, deterministic_session_id(task_context)) + await session.seed(session_root) + return session + except Exception: # pragma: no cover - durability must never break the run + logger.warning("Could not wire a durable DeepSeek session; continuing without resume.") + return None diff --git a/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_memory.py b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_memory.py new file mode 100644 index 000000000..63ff7c3bf --- /dev/null +++ b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_memory.py @@ -0,0 +1,88 @@ +"""Cross-run harness memory — a session archive in a keyed `MemoryStore`. + +The per-run crash-resume session (see `._durable`) is keyed by the action and +backed by a `flyte.Checkpoint` — ephemeral, scoped to one run. This module keys +the same session archive by a stable `memory_key` (a user/thread id) and backs it +with the durable, cross-run `flyte.ai.agents.memory.MemoryStore`, so a later run +with the same key resumes the prior conversation: the harness sees its own JSONL +transcript for a session id it has already used, and continues it. + +Because the memory store survives retries too, it subsumes crash-resume: when a +`memory_key` is given, the adapter uses this session instead of the checkpoint one. +""" + +from __future__ import annotations + +import pathlib +import typing +import uuid + +from flyte._logging import logger +from flyteplugins.agents.core import resolve_memory + +from ._durable import restore, snapshot + +# Fixed namespace so a memory key maps to a stable harness session id across runs. +_SESSION_NS = uuid.UUID("2c7f4b81-9e05-5a62-b7d3-8f1a6c9e2b40") + +# Path-addressed slot holding the thread's session archive inside the MemoryStore. +_SESSION_PATH = "deepseek/session.json" + + +def memory_session_id(memory_key: str) -> str: + """A stable harness session id for a memory key (same across runs).""" + return f"flyte-mem-{uuid.uuid5(_SESSION_NS, memory_key).hex}" + + +class MemorySession: + """A harness session whose `session_root` is mirrored to a keyed `MemoryStore`. + + Mirrors `flyteplugins.agents.deepseek._durable.CheckpointSession`, so + `run_agent` drives either one through the same `seed` / `persist` pair. + """ + + def __init__(self, store: typing.Any, session_id: str) -> None: + self._store = store + self.session_id = session_id + self.resumed = False + + async def seed(self, session_root: pathlib.Path) -> bool: + """Restore the thread's prior session files into `session_root`.""" + try: + archive = await self._store.read_json.aio(_SESSION_PATH, {}) + except Exception: # pragma: no cover - memory is best-effort, never fatal + logger.warning("Could not load DeepSeek cross-run memory; continuing without prior history.") + return False + self.resumed = restore(session_root, archive or {}) + return self.resumed + + async def persist(self, session_root: pathlib.Path) -> None: + """Persist `session_root` back to the keyed store. Never raises.""" + try: + archive = snapshot(session_root) + if not archive: + return + await self._store.write_json.aio(_SESSION_PATH, archive, actor="deepseek-harness") + await self._store.save.aio() + except Exception: # pragma: no cover - memory is best-effort, never fatal + logger.warning("Could not persist DeepSeek cross-run memory; continuing.") + + +async def wire_memory_session(session_root: pathlib.Path, *, memory_key: str | None) -> MemorySession | None: + """Build a memory-backed session for `memory_key`, seeded from prior runs. + + Returns `None` when memory is off or unavailable (no key, no durable store), + in which case the caller falls back to the per-run durable session. Never raises. + """ + if not memory_key: + return None + try: + store = await resolve_memory(memory_key) + if store is None: + return None + session = MemorySession(store, memory_session_id(memory_key)) + await session.seed(session_root) + return session + except Exception: # pragma: no cover - memory is best-effort, never fatal + logger.warning("Could not wire DeepSeek cross-run memory for key %r; continuing without it.", memory_key) + return None diff --git a/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_run.py b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_run.py new file mode 100644 index 000000000..358842474 --- /dev/null +++ b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_run.py @@ -0,0 +1,360 @@ +"""`run_agent` — run a DeepSeek Harness agent on Flyte. + +DeepSeek Harness owns the loop: its runtime subprocess drives the model and its +own tools (bash, string editor, ...) over JSON-RPC stdio. `run_agent` runs that +loop inside your `@env.task`: it prepares the workspace and session store, +publishes your Flyte-task tools into the workspace (see `._bridge`), drives +`DeepSeekHarness.run` — a blocking call, bridged off the event loop with +`asyncio.to_thread` so the bridge can serve tool calls while it runs — and +returns the final response. + +Durability: tool calls are durable Flyte child actions (see +`flyteplugins.agents.deepseek.tool`). Per-turn model replay is not available +here — the model loop runs inside the harness runtime (a subprocess Flyte +doesn't intercept), so a model turn can't be a `flyte.trace` leaf the way it is +for client-side SDKs. Instead, `durable=True` wires the harness's own JSONL +session store onto a `flyte.Checkpoint` (see `._durable`), so a crashed +attempt's conversation is restored on retry rather than restarted. Tool +durability, retries and caching apply regardless. + +Observability: the harness streams session events as JSON-RPC notifications; +those are rendered into the Flyte report timeline alongside the per-tool +outcomes recorded by the bridge. +""" + +from __future__ import annotations + +import asyncio +import os +import pathlib +import shutil +import tempfile +import typing + +from deepseek_harness import DeepSeekHarness, DeepSeekHarnessConfig, Notification, RunResult +from flyte._logging import logger +from flyte._task import TaskTemplate +from flyteplugins.agents.core import ( + ReportTimeline, + abbrev, + apply_call_wrapper, + apply_instrumentation, + flush_report, + sync_variant, +) + +from ._bridge import ToolBridge +from ._durable import wire_durable_session +from ._memory import wire_memory_session +from ._tools import HarnessTool, tool + + +def _coerce_tool(t: typing.Any) -> HarnessTool: + if isinstance(t, HarnessTool): + return t + if isinstance(t, TaskTemplate): + return tool(t) # type: ignore[return-value] + return tool(t) # type: ignore[return-value] + + +def _harness_run(harness: DeepSeekHarness, prompt: str, **kwargs: typing.Any) -> RunResult: + """The SDK call itself, isolated so an observability library can wrap it.""" + return harness.run(prompt, **kwargs) + + +async def run_agent( + input: str, + *, + tools: typing.Sequence[typing.Any] = (), + model: str | None = None, + instructions: str | None = None, + durable: bool = True, + observability: bool = True, + memory_key: str | None = None, + provider: str | None = None, + max_tokens: int | None = None, + workspace: str | os.PathLike | None = None, + session_id: str | None = None, + config: DeepSeekHarnessConfig | None = None, + **harness_kwargs: typing.Any, +) -> str: + """Run a DeepSeek Harness agent with the given tools and prompt; return the final text. + + Await this from an async task as `await run_agent(...)`; from a sync task + use `flyteplugins.agents.deepseek.run_agent_sync` instead. + + Call this from inside an `@env.task` — that task is the durable parent, and + each tool the agent calls runs as a durable Flyte child action. Give the + enclosing task `retries=...` for self-healing and `report=True` to see the + agent timeline. + + Pass a fully-built `DeepSeekHarnessConfig` via `config` to keep SDK-native + configuration (a custom `cordis` composition, `base_url` / `api_key`, + timeouts); `model` / `provider` / `max_tokens` / `workspace` are layered on + top of it, and any extra `**harness_kwargs` are applied last. + + Args: + input: The user prompt. + tools: `tool`-wrapped tools or bare `@env.task` templates. They are + published into the harness workspace as executable shims and + described to the model in its instructions, because the harness has + no tool-registration channel of its own — see `._bridge`. + model: Model id resolved by the configured provider. `None` leaves the + SDK default (`deepseek-v4-flash`) in place. + instructions: System prompt, passed to the runtime as `DSH_SYSTEM_PROMPT`. + durable: Wire the harness's JSONL session store onto a `flyte.Checkpoint` + so a retry resumes the conversation instead of restarting it. + observability: Render the run timeline into the Flyte task report. + memory_key: Stable id (a user/thread id) for cross-run memory. When set, + the session is persisted to a keyed `MemoryStore` and resumed on a + later run with the same key. This also covers crash-resume, so it + takes precedence over the per-run `durable` checkpoint. + provider: Provider route registered by the Cordis composition. + max_tokens: Per-request output-token cap for the root agent. + workspace: Directory the harness works in (its bash cwd). Defaults to a + fresh temp directory removed when the run ends; pass a path — for + example a `flyte.io.Dir` you downloaded — to work on real files. + session_id: Override the session id. Defaults to the durable/memory + session id, so retries and same-key runs continue one conversation. + config: A pre-built `DeepSeekHarnessConfig` to layer the above onto. + **harness_kwargs: Extra `DeepSeekHarnessConfig` fields (e.g. `cordis=`, + `base_url=`, `api_key=`, `request_timeout_seconds=`). + + Returns: + The agent's final response as a string. + """ + harness_tools = [_coerce_tool(t) for t in tools] + + owned_workspace = workspace is None + workspace_path = _prepare_workspace(workspace) + session_root = pathlib.Path(tempfile.mkdtemp(prefix="flyte-dsh-sessions-")) + + # Memory subsumes crash-resume: a keyed store survives retries too, so when a + # memory_key is given it replaces the per-run checkpoint session. + session = await wire_memory_session(session_root, memory_key=memory_key) + if session is None: + session = await wire_durable_session(session_root, durable=durable) + + timeline = ReportTimeline() if observability else None + if timeline is not None: + timeline.heading("DeepSeek Harness agent") + + bridge = ToolBridge(harness_tools, timeline=timeline) + try: + await bridge.start(workspace_path) + + cfg = _build_config( + config, + model=model, + provider=provider, + max_tokens=max_tokens, + instructions=instructions, + cwd=str(workspace_path), + session_root=str(session_root), + extra=harness_kwargs, + ) + # The SDK carries its configuration on this object, so that is what is offered + # to any registered instrumentor. Unregistered, it comes back unchanged. + cfg = apply_instrumentation("deepseek", cfg) + + prompt = _compose_prompt(input, bridge.instructions()) + run_session_id = session_id or (session.session_id if session is not None else None) + + result = await _drive(cfg, prompt, run_session_id, timeline) + + if timeline is not None: + _render_result(timeline, result) + if session is not None: + await session.persist(session_root) + return result.final_response or "" + finally: + await bridge.stop() + shutil.rmtree(session_root, ignore_errors=True) + if owned_workspace: + shutil.rmtree(workspace_path, ignore_errors=True) + elif bridge.tools_dir is not None: + # A caller-owned workspace is left as we found it, minus our shims. + shutil.rmtree(bridge.tools_dir, ignore_errors=True) + if observability: + await flush_report() + + +run_agent_sync = sync_variant(run_agent) + + +async def _drive( + cfg: DeepSeekHarnessConfig, + prompt: str, + session_id: str | None, + timeline: ReportTimeline | None, +) -> RunResult: + """Run the (blocking) harness off the event loop, streaming events to the report. + + `DeepSeekHarness.run` blocks until the agent goes idle, so it runs on a worker + thread — that keeps the loop free to serve the tool bridge, which is what makes + tool calls possible at all. Notifications arrive on that worker thread, so each + is marshalled back onto the loop before touching the report. + """ + loop = asyncio.get_running_loop() + on_notification = _notification_sink(loop, timeline) if timeline is not None else None + + def _blocking() -> RunResult: + with DeepSeekHarness(cfg) as harness: + run = apply_call_wrapper("deepseek", _harness_run) + kwargs: dict[str, typing.Any] = {"on_notification": on_notification} + if session_id is not None: + kwargs["session_id"] = session_id + return run(harness, prompt, **kwargs) + + return await asyncio.to_thread(_blocking) + + +def _prepare_workspace(workspace: str | os.PathLike | None) -> pathlib.Path: + """Resolve (or create) the directory the harness works in. + + Kept sync — local filesystem setup, done once before the run starts. + """ + path = pathlib.Path(workspace).resolve() if workspace else pathlib.Path(tempfile.mkdtemp(prefix="flyte-dsh-ws-")) + path.mkdir(parents=True, exist_ok=True) + return path + + +def _build_config( + base: DeepSeekHarnessConfig | None, + *, + model: str | None, + provider: str | None, + max_tokens: int | None, + instructions: str | None, + cwd: str, + session_root: str, + extra: dict[str, typing.Any], +) -> DeepSeekHarnessConfig: + """Layer the adapter's options onto a (possibly caller-supplied) SDK config. + + The adapter owns `cwd` and `session_root`: the workspace is where the tool + shims are published, and the session root is what the durable/memory session + mirrors. Everything else is the caller's. + """ + fields = {f: getattr(base, f) for f in DeepSeekHarnessConfig.__dataclass_fields__} if base else {} + fields.update(extra) + if model is not None: + fields["model"] = model + if provider is not None: + fields["provider"] = provider + if max_tokens is not None: + fields["max_tokens"] = max_tokens + fields["cwd"] = cwd + fields["session_root"] = session_root + if instructions is not None: + fields["env"] = {**(fields.get("env") or {}), "DSH_SYSTEM_PROMPT": instructions} + return DeepSeekHarnessConfig(**fields) + + +def _compose_prompt(input: str, tool_manual: str) -> str: + """Prefix the tool manual to the prompt, so the model learns what it can call.""" + return f"{tool_manual}\n\n---\n\n{input}" if tool_manual else input + + +def _notification_sink( + loop: asyncio.AbstractEventLoop, + timeline: ReportTimeline, +) -> typing.Callable[[Notification], None]: + """Build the `on_notification` callback that renders session events to the report.""" + + def sink(notification: Notification) -> None: + if notification.method != "session.event": + return + event = notification.payload.get("event") + if isinstance(event, dict): + loop.call_soon_threadsafe(_render_event, timeline, event) + + return sink + + +def _render_event(timeline: ReportTimeline, event: dict[str, typing.Any]) -> None: + """Render one harness session event as a timeline row (best-effort). + + Maps the runtime's `SessionEventMap` types onto timeline rows. The harness's + own tools (bash, editor) show up as `tool/call` + `tool/result`; Flyte-task + tools are recorded separately by the bridge, which sees their arguments and + results directly. + """ + try: + kind = str(event.get("type") or "") + data = event.get("data") if isinstance(event.get("data"), dict) else {} + if kind == "assistant/message": + text = _assistant_text(data) + if text.strip(): + timeline.row(icon="💬", label="assistant", meta=_fmt_usage(data.get("usage")), detail=abbrev(text, 200)) + elif kind == "tool/call": + timeline.row( + icon="🧰", + label=str(data.get("name") or "tool"), + meta="harness tool", + detail=abbrev(data.get("arguments"), 160), + ) + elif kind == "tool/result": + error = data.get("error") if isinstance(data.get("error"), dict) else None + timeline.row( + icon="❌" if error else "🔧", + label=str((error or {}).get("name") or "tool"), + meta="harness tool result", + detail=abbrev(data.get("message"), 160), + error="error" if error else None, + ) + elif kind == "turn/end": + reason = data.get("reason") if isinstance(data.get("reason"), dict) else {} + timeline.row(icon="↩️", label="turn", meta=str(reason.get("kind") or "")) + except Exception: # pragma: no cover - observability must never break the run + logger.debug("Could not render a DeepSeek harness event", exc_info=True) + + +def _compact(n: int) -> str: + """Compact a token count, e.g. 5000 -> '5.0k'.""" + return f"{n / 1000:.1f}k" if n >= 1000 else str(n) + + +def _fmt_usage(usage: typing.Any) -> str: + """A compact token breakdown from an assistant turn's `TokenUsage`, if present.""" + if not isinstance(usage, dict): + return "" + + def pick(*keys: str) -> int: + for key in keys: + value = usage.get(key) + if isinstance(value, (int, float)) and value: + return int(value) + return 0 + + fields = [ + ("in", pick("input_tokens", "inputTokens", "promptTokens")), + ("out", pick("output_tokens", "outputTokens", "completionTokens")), + ("cache read", pick("cache_read_input_tokens", "cacheReadInputTokens")), + ] + return " · ".join(f"{label} {_compact(n)}" for label, n in fields if n) + + +def _assistant_text(data: dict[str, typing.Any]) -> str: + """Concatenate the text blocks of an `assistant/message` event.""" + message = data.get("message") + owner = message if isinstance(message, dict) else data + content = owner.get("content") + if not isinstance(content, list): + return "" + return "".join( + str(block.get("text") or "") for block in content if isinstance(block, dict) and block.get("type") == "text" + ) + + +def _render_result(timeline: ReportTimeline, result: RunResult) -> None: + parts = [f"{len(result.events)} events"] + if result.finish_reason: + parts.append(result.finish_reason) + timeline.row( + icon="✅", + label="result", + meta=" · ".join(parts), + detail=abbrev(result.final_response, 200), + error="error" if result.finish_reason == "error" else None, + ) diff --git a/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_tools.py b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_tools.py new file mode 100644 index 000000000..62100c5e0 --- /dev/null +++ b/plugins/agents/deepseek/src/flyteplugins/agents/deepseek/_tools.py @@ -0,0 +1,165 @@ +"""Turn Flyte tasks into DeepSeek Harness tools that execute as durable actions. + +Unlike the client-side SDKs, DeepSeek Harness does not accept Python callables as +tools: the model loop and its whole tool surface live inside the harness runtime +subprocess, composed from Cordis plugins (bash, string editor, ...). The Python +SDK's wire protocol (`initialize` / `session/prompt`) has no tool-registration +message, so there is nothing to hand a Python function to. + +What the harness *does* give every composition is local bash in a workspace +directory we choose. So a Flyte-task tool is published into that workspace as a +small executable shim, and the shim calls back into this process (see +`._bridge`) to run the real task. From the model's point of view it is an +ordinary command it can run; from Flyte's point of view the call is a durable +child action with its own container/resources, retries and caching — the same +guarantee every other adapter's `tool` provides. + +`HarnessTool` is that published tool: a name, a description, a JSON schema +derived from the task via the Flyte type engine, and the coroutine the bridge +dispatches to. +""" + +from __future__ import annotations + +import inspect +import json +import typing +from functools import partial + +from flyte._task import AsyncFunctionTaskTemplate +from flyte.models import NativeInterface +from flyteplugins.agents.core import attach_tool_resolver, coerce_tool_args, task_json_schema + + +class HarnessTool: + """A tool the DeepSeek Harness runtime can call, backed by a Flyte task. + + The adapter publishes one bash shim per tool into the harness workspace and + dispatches the shim's callback to `invoke`. + """ + + def __init__( + self, + name: str, + description: str, + schema: dict[str, typing.Any], + handler: typing.Callable[[dict[str, typing.Any]], typing.Awaitable[typing.Any]], + ) -> None: + self.name = name + self.description = description + self.schema = schema + self._handler = handler + + async def invoke(self, args: dict[str, typing.Any]) -> str: + """Run the tool and render its result as text for the model.""" + return _as_text(await self._handler(args or {})) + + def usage(self) -> str: + """The line the model is shown for this tool, in its instructions.""" + params = self.schema.get("properties") or {} + required = set(self.schema.get("required") or ()) + rendered = ", ".join( + f"{key}: {_type_name(spec)}" + ("" if key in required else " (optional)") for key, spec in params.items() + ) + return f"{self.name}({rendered}) — {self.description}" + + def __repr__(self) -> str: # pragma: no cover - debugging aid + return f"HarnessTool(name={self.name!r})" + + +def tool( + func: AsyncFunctionTaskTemplate | typing.Callable | None = None, + *, + name: str | None = None, + description: str | None = None, +) -> HarnessTool | typing.Callable: + """Convert a Flyte task (or plain callable) into a DeepSeek Harness tool. + + - For an `@env.task`: returns a `HarnessTool` that runs the task as a durable + Flyte child action when the harness calls it. The input schema is derived + from the task via the Flyte type engine. The backing task is wired to + `flyteplugins.agents.core.ToolTaskResolver` and exposed via + `__wrapped_task__` so it resolves to itself on the worker (no recursion). + - For a plain (async) callable: returns a `HarnessTool` that runs it inline. + + Usable bare, parametrized, or as a direct call: + + ```python + @tool + @env.task + async def get_weather(city: str) -> str: ... + ``` + """ + if func is None: + return partial(tool, name=name, description=description) + if isinstance(func, AsyncFunctionTaskTemplate): + return _task_to_tool(func, name=name, description=description) + if not callable(func): + raise TypeError(f"tool() expects a Flyte @env.task or a callable, got {type(func).__name__!r}.") + return _callable_to_tool(func, name=name, description=description) + + +def _task_to_tool( + task: AsyncFunctionTaskTemplate, + *, + name: str | None = None, + description: str | None = None, +) -> HarnessTool: + tool_name = name or task.func.__name__ + desc = (description or task.func.__doc__ or f"Run {tool_name}").strip() + + async def handler(args: dict[str, typing.Any]) -> typing.Any: + # In a Flyte task context this submits a durable child action; locally it + # runs inline. ``coerce_tool_args`` relaxes LLM int->float args so Flyte's + # type engine doesn't reject e.g. ``amount_usd=42`` for a ``float`` param. + return await task.aio(**coerce_tool_args(task, args or {})) + + harness_tool = HarnessTool(tool_name, desc, task_json_schema(task), handler) + + # The tool shadows the task at module scope, so wire the shared resolver and + # expose the real task for it to recover on the worker. + attach_tool_resolver(task) + harness_tool.__wrapped_task__ = task # type: ignore[attr-defined] + harness_tool.task = task # type: ignore[attr-defined] + return harness_tool + + +def _callable_to_tool( + func: typing.Callable, + *, + name: str | None = None, + description: str | None = None, +) -> HarnessTool: + tool_name = name or getattr(func, "__name__", "tool") + desc = (description or func.__doc__ or f"Run {tool_name}").strip() + schema = NativeInterface.from_callable(func).json_schema + + async def handler(args: dict[str, typing.Any]) -> typing.Any: + out = func(**(args or {})) + if inspect.isawaitable(out): + out = await out + return out + + return HarnessTool(tool_name, desc, schema, handler) + + +def _type_name(spec: typing.Any) -> str: + """A short type label for a property in the tool's JSON schema.""" + if not isinstance(spec, dict): + return "any" + if "enum" in spec: + return " | ".join(json.dumps(value) for value in spec["enum"]) + declared = spec.get("type") + if isinstance(declared, list): + return " | ".join(str(entry) for entry in declared) + return str(declared or "any") + + +def _as_text(result: typing.Any) -> str: + """Render a tool result as the text the harness reads back from the shim.""" + if isinstance(result, str): + return result + try: + return json.dumps(result, default=str) + except (TypeError, ValueError): # pragma: no cover - exotic result type + return str(result) diff --git a/plugins/agents/deepseek/tests/__init__.py b/plugins/agents/deepseek/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/agents/deepseek/tests/test_conformance.py b/plugins/agents/deepseek/tests/test_conformance.py new file mode 100644 index 000000000..9d23afe35 --- /dev/null +++ b/plugins/agents/deepseek/tests/test_conformance.py @@ -0,0 +1,14 @@ +"""The deepseek adapter must satisfy the shared agent-adapter contract. + +This is the enforcement: if the adapter drifts from the common format (missing +``tool``/``run_agent``, wrong ``run_agent`` keyword surface, or a tool task that +isn't wired to the resolver), CI fails here. +""" + +from flyteplugins.agents.core.testing import assert_adapter_conforms + +import flyteplugins.agents.deepseek as adapter + + +def test_deepseek_adapter_conforms(): + assert_adapter_conforms(adapter) diff --git a/plugins/agents/deepseek/tests/test_deepseek_bridge.py b/plugins/agents/deepseek/tests/test_deepseek_bridge.py new file mode 100644 index 000000000..e1ca7fb3a --- /dev/null +++ b/plugins/agents/deepseek/tests/test_deepseek_bridge.py @@ -0,0 +1,204 @@ +"""Tests for the tool bridge — the seam that lets the harness call Flyte tasks. + +These run the published shim as a real subprocess, exactly as the harness's bash +tool would, so the whole path is exercised: shim -> unix socket -> bridge -> +``task.aio`` -> result text on stdout. No harness runtime and no network needed. +""" + +import asyncio +import json +import os +import stat +import subprocess +from unittest.mock import AsyncMock, patch + +import flyte +import pytest + +from flyteplugins.agents.deepseek import TOOLS_DIRNAME, ToolBridge, tool + + +def _weather_tool(env_name): + env = flyte.TaskEnvironment(env_name) + + @tool + @env.task + async def get_weather(city: str) -> str: + """Get the current weather for a city.""" + return f"sunny in {city}" + + return get_weather + + +async def _call_shim(workspace, name, args, timeout=30): + """Run a published shim the way the harness's bash tool would.""" + shim = workspace / TOOLS_DIRNAME / name + return await asyncio.to_thread( + subprocess.run, + [str(shim), json.dumps(args)], + capture_output=True, + text=True, + timeout=timeout, + ) + + +@pytest.mark.asyncio +async def test_start_publishes_an_executable_shim_per_tool(tmp_path): + bridge = ToolBridge([_weather_tool("ds_bridge_a")]) + try: + await bridge.start(tmp_path) + shim = tmp_path / TOOLS_DIRNAME / "get_weather" + assert shim.is_file() + assert shim.stat().st_mode & stat.S_IXUSR # bash has to be able to run it + assert os.path.exists(bridge.socket_path) + finally: + await bridge.stop() + + +@pytest.mark.asyncio +async def test_shim_subprocess_round_trips_to_the_flyte_task(tmp_path): + """The end-to-end path the model actually takes.""" + weather = _weather_tool("ds_bridge_b") + bridge = ToolBridge([weather]) + try: + await bridge.start(tmp_path) + with patch.object(weather.task, "aio", new_callable=AsyncMock, return_value="sunny in Paris") as mock_aio: + done = await _call_shim(tmp_path, "get_weather", {"city": "Paris"}) + finally: + await bridge.stop() + + assert done.returncode == 0, done.stderr + assert done.stdout.strip() == "sunny in Paris" + # The call reached the task through ``aio`` — i.e. as a durable child action. + mock_aio.assert_awaited_once_with(city="Paris") + + +@pytest.mark.asyncio +async def test_concurrent_tool_calls_are_served_together(tmp_path): + """Each call is its own connection, so the harness can fan out.""" + weather = _weather_tool("ds_bridge_c") + bridge = ToolBridge([weather]) + try: + await bridge.start(tmp_path) + with patch.object(weather.task, "aio", new_callable=AsyncMock, side_effect=lambda city: f"sunny in {city}"): + results = await asyncio.gather( + _call_shim(tmp_path, "get_weather", {"city": "Paris"}), + _call_shim(tmp_path, "get_weather", {"city": "Tokyo"}), + _call_shim(tmp_path, "get_weather", {"city": "Lima"}), + ) + finally: + await bridge.stop() + + assert [r.returncode for r in results] == [0, 0, 0] + assert sorted(r.stdout.strip() for r in results) == ["sunny in Lima", "sunny in Paris", "sunny in Tokyo"] + + +@pytest.mark.asyncio +async def test_a_failing_task_is_reported_to_the_model_not_raised(tmp_path): + """A tool error becomes a non-zero exit + stderr, so the agent can react.""" + weather = _weather_tool("ds_bridge_d") + bridge = ToolBridge([weather]) + try: + await bridge.start(tmp_path) + boom = RuntimeError("upstream is down") + with patch.object(weather.task, "aio", new_callable=AsyncMock, side_effect=boom): + done = await _call_shim(tmp_path, "get_weather", {"city": "Paris"}) + finally: + await bridge.stop() + + assert done.returncode == 1 + assert "RuntimeError: upstream is down" in done.stderr + + +@pytest.mark.asyncio +async def test_invalid_json_arguments_are_rejected_by_the_shim(tmp_path): + bridge = ToolBridge([_weather_tool("ds_bridge_e")]) + try: + await bridge.start(tmp_path) + shim = tmp_path / TOOLS_DIRNAME / "get_weather" + done = await asyncio.to_thread( + subprocess.run, [str(shim), "not json"], capture_output=True, text=True, timeout=30 + ) + finally: + await bridge.stop() + + assert done.returncode == 2 + assert "must be a JSON object" in done.stderr + + +@pytest.mark.asyncio +async def test_unknown_tool_is_answered_with_the_available_names(tmp_path): + bridge = ToolBridge([_weather_tool("ds_bridge_f")]) + try: + await bridge.start(tmp_path) + response = await bridge._dispatch({"tool": "nope", "args": {}}) + finally: + await bridge.stop() + + assert response["ok"] is False + assert "unknown tool 'nope'" in response["error"] + assert "get_weather" in response["error"] + + +@pytest.mark.asyncio +async def test_stop_removes_the_socket(tmp_path): + bridge = ToolBridge([_weather_tool("ds_bridge_g")]) + await bridge.start(tmp_path) + socket_path = bridge.socket_path + await bridge.stop() + assert not os.path.exists(socket_path) + + +@pytest.mark.asyncio +async def test_no_tools_means_no_socket_and_no_instructions(tmp_path): + bridge = ToolBridge([]) + try: + await bridge.start(tmp_path) + assert bridge.socket_path is None + assert bridge.instructions() == "" + assert not (tmp_path / TOOLS_DIRNAME).exists() + finally: + await bridge.stop() + + +@pytest.mark.asyncio +async def test_instructions_tell_the_model_how_to_call_each_tool(tmp_path): + """The harness has no tool-declaration channel, so this prose is the contract.""" + env = flyte.TaskEnvironment("ds_bridge_h") + + @tool + @env.task + async def book(city: str, nights: int) -> str: + """Book a stay.""" + return city + + bridge = ToolBridge([book]) + try: + await bridge.start(tmp_path) + manual = bridge.instructions() + finally: + await bridge.stop() + + assert "book(city: string, nights: integer) — Book a stay." in manual + assert f'{TOOLS_DIRNAME}/book \'{{"city": "...", "nights": 0}}\'' in manual + + +@pytest.mark.asyncio +async def test_tool_calls_are_recorded_on_the_timeline(tmp_path): + weather = _weather_tool("ds_bridge_i") + rows = [] + + class RecordingTimeline: + def row(self, **kwargs): + rows.append(kwargs) + + bridge = ToolBridge([weather], timeline=RecordingTimeline()) + try: + await bridge.start(tmp_path) + with patch.object(weather.task, "aio", new_callable=AsyncMock, return_value="sunny in Paris"): + await bridge._dispatch({"tool": "get_weather", "args": {"city": "Paris"}}) + finally: + await bridge.stop() + + assert [r["meta"] for r in rows] == ["tool", "tool result"] + assert all(r["label"] == "get_weather" for r in rows) diff --git a/plugins/agents/deepseek/tests/test_deepseek_durable.py b/plugins/agents/deepseek/tests/test_deepseek_durable.py new file mode 100644 index 000000000..a7d3136ae --- /dev/null +++ b/plugins/agents/deepseek/tests/test_deepseek_durable.py @@ -0,0 +1,173 @@ +"""Tests for durable harness sessions — checkpoint-backed session resume. + +Offline: a ``FakeCheckpoint`` stands in for ``flyte.Checkpoint`` (``save`` writes +this attempt's blob, ``load`` reads the previous attempt's), so the +first-attempt-starts / retry-resumes behavior is exercised without a backend. +""" + +import pathlib +from types import SimpleNamespace +from unittest.mock import patch + +import pytest + +from flyteplugins.agents.deepseek._durable import ( + CheckpointSession, + deterministic_session_id, + restore, + snapshot, + wire_durable_session, +) + + +def _ctx(run_name="run-1", name="act-1", checkpoint=None): + action = SimpleNamespace(run_name=run_name, name=name) + return SimpleNamespace(action=action, task_action=None, checkpoint=checkpoint) + + +class FakeCheckpoint: + """Mimics flyte.Checkpoint: ``save`` -> this attempt's dir, ``load`` -> previous.""" + + def __init__(self, save_dir, prev_dir=None): + self.save_dir = pathlib.Path(save_dir) + self.prev_dir = pathlib.Path(prev_dir) if prev_dir else None + + async def save(self, data): + self.save_dir.mkdir(parents=True, exist_ok=True) + (self.save_dir / "payload").write_bytes(data if isinstance(data, bytes) else str(data).encode()) + + async def load(self): + if self.prev_dir is not None and (self.prev_dir / "payload").is_file(): + return self.prev_dir + return None + + +def test_deterministic_session_id_is_stable_and_per_action(): + tc = _ctx() + sid = deterministic_session_id(tc) + assert sid == deterministic_session_id(tc) # stable -> same across retries + assert deterministic_session_id(_ctx(name="act-2")) != sid # per-action + + +def test_deterministic_session_id_prefers_task_action(): + """Inside a @trace pseudo-action the id must still track the real task.""" + pinned = SimpleNamespace(run_name="r", name="real-task") + via_task_action = SimpleNamespace(action=SimpleNamespace(run_name="r", name="trace-pseudo"), task_action=pinned) + via_action = SimpleNamespace(action=pinned, task_action=None) + assert deterministic_session_id(via_task_action) == deterministic_session_id(via_action) + + +def test_snapshot_and_restore_round_trip_a_session_tree(tmp_path): + source = tmp_path / "sessions" + (source / "nested").mkdir(parents=True) + (source / "s1.jsonl").write_text('{"type":"assistant/message"}\n') + (source / "nested" / "s2.jsonl").write_text('{"type":"turn/end"}\n') + + archive = snapshot(source) + assert set(archive) == {"s1.jsonl", str(pathlib.Path("nested") / "s2.jsonl")} + + target = tmp_path / "restored" + target.mkdir() + assert restore(target, archive) is True + assert (target / "s1.jsonl").read_text() == '{"type":"assistant/message"}\n' + assert (target / "nested" / "s2.jsonl").read_text() == '{"type":"turn/end"}\n' + + +def test_snapshot_of_a_missing_root_is_empty(tmp_path): + assert snapshot(tmp_path / "nope") == {} + assert restore(tmp_path, {}) is False + + +def test_snapshot_skips_unreadable_entries(tmp_path): + """Best-effort: a binary transcript must not fail the run that produced it.""" + source = tmp_path / "sessions" + source.mkdir() + (source / "good.jsonl").write_text("{}\n") + (source / "binary.bin").write_bytes(b"\xff\xfe\x00") + assert set(snapshot(source)) == {"good.jsonl"} + + +@pytest.mark.asyncio +async def test_session_round_trips_across_attempts(tmp_path): + """Attempt 1 persists its transcript; attempt 2 seeds from it and reports resume.""" + first_root = tmp_path / "run1" + first_root.mkdir() + (first_root / "s.jsonl").write_text('{"turn": 1}\n') + + blob = tmp_path / "ckpt" + await CheckpointSession(FakeCheckpoint(blob), "sid").persist(first_root) + + second_root = tmp_path / "run2" + second_root.mkdir() + resumed = CheckpointSession(FakeCheckpoint(tmp_path / "ckpt2", prev_dir=blob), "sid") + assert await resumed.seed(second_root) is True + assert resumed.resumed is True + assert (second_root / "s.jsonl").read_text() == '{"turn": 1}\n' + + +@pytest.mark.asyncio +async def test_first_attempt_has_nothing_to_seed(tmp_path): + session = CheckpointSession(FakeCheckpoint(tmp_path / "ckpt"), "sid") + assert await session.seed(tmp_path) is False + assert session.resumed is False + + +@pytest.mark.asyncio +async def test_persist_never_raises(tmp_path): + """Durability is best-effort: a failing checkpoint must not break the run.""" + + class BrokenCheckpoint(FakeCheckpoint): + async def save(self, data): + raise OSError("blob store unavailable") + + (tmp_path / "s.jsonl").write_text("{}\n") + await CheckpointSession(BrokenCheckpoint(tmp_path / "ckpt"), "sid").persist(tmp_path) + + +@pytest.mark.asyncio +async def test_wire_pins_the_action_session_id(tmp_path): + tc = _ctx(checkpoint=FakeCheckpoint(tmp_path / "ckpt")) + with patch("flyte.ctx", return_value=tc): + session = await wire_durable_session(tmp_path, durable=True) + assert session is not None + assert session.session_id == deterministic_session_id(tc) + assert session.resumed is False + + +@pytest.mark.asyncio +async def test_wire_on_retry_seeds_the_prior_transcript(tmp_path): + blob = tmp_path / "ckpt" + first_root = tmp_path / "run1" + first_root.mkdir() + (first_root / "s.jsonl").write_text('{"turn": 1}\n') + await CheckpointSession(FakeCheckpoint(blob), "sid").persist(first_root) + + second_root = tmp_path / "run2" + second_root.mkdir() + tc = _ctx(checkpoint=FakeCheckpoint(tmp_path / "ckpt2", prev_dir=blob)) + with patch("flyte.ctx", return_value=tc): + session = await wire_durable_session(second_root, durable=True) + + assert session is not None + assert session.resumed is True + # The retry targets the same session the crashed attempt was building. + assert session.session_id == deterministic_session_id(tc) + assert (second_root / "s.jsonl").read_text() == '{"turn": 1}\n' + + +@pytest.mark.asyncio +async def test_wire_is_noop_when_durable_false(tmp_path): + assert await wire_durable_session(tmp_path, durable=False) is None + + +@pytest.mark.asyncio +async def test_wire_is_noop_without_task_context(tmp_path): + with patch("flyte.ctx", return_value=None): + assert await wire_durable_session(tmp_path, durable=True) is None + + +@pytest.mark.asyncio +async def test_wire_is_noop_without_a_checkpoint(tmp_path): + """Running locally there is no checkpoint; the run proceeds without resume.""" + with patch("flyte.ctx", return_value=_ctx(checkpoint=None)): + assert await wire_durable_session(tmp_path, durable=True) is None diff --git a/plugins/agents/deepseek/tests/test_deepseek_memory.py b/plugins/agents/deepseek/tests/test_deepseek_memory.py new file mode 100644 index 000000000..eb693f63f --- /dev/null +++ b/plugins/agents/deepseek/tests/test_deepseek_memory.py @@ -0,0 +1,112 @@ +"""Tests for cross-run memory — the session archive in a keyed MemoryStore. + +Offline: a ``FakeStore`` stands in for ``flyte.ai.agents.memory.MemoryStore``, +exposing the ``read_json`` / ``write_json`` / ``save`` surface the adapter uses +(each with the ``.aio`` companion the real store has). +""" + +from unittest.mock import patch + +import pytest + +from flyteplugins.agents.deepseek._memory import MemorySession, memory_session_id, wire_memory_session + + +class _Method: + """A store method exposing the ``.aio`` companion the real MemoryStore has.""" + + def __init__(self, fn): + self.aio = fn + + +class FakeStore: + def __init__(self, initial=None): + self.data = dict(initial or {}) + self.saved = 0 + self.read_json = _Method(self._read_json) + self.write_json = _Method(self._write_json) + self.save = _Method(self._save) + + async def _read_json(self, path, default=None): + return self.data.get(path, default) + + async def _write_json(self, path, value, actor=None): + self.data[path] = value + self.actor = actor + + async def _save(self): + self.saved += 1 + + +def test_memory_session_id_is_stable_per_key(): + assert memory_session_id("user-1") == memory_session_id("user-1") + assert memory_session_id("user-1") != memory_session_id("user-2") + + +@pytest.mark.asyncio +async def test_session_continues_across_runs(tmp_path): + """Run 1 persists the transcript; run 2 with the same key resumes it.""" + store = FakeStore() + + first_root = tmp_path / "run1" + first_root.mkdir() + (first_root / "s.jsonl").write_text('{"turn": 1}\n') + await MemorySession(store, "sid").persist(first_root) + assert store.saved == 1 + + second_root = tmp_path / "run2" + second_root.mkdir() + later = MemorySession(store, "sid") + assert await later.seed(second_root) is True + assert (second_root / "s.jsonl").read_text() == '{"turn": 1}\n' + + +@pytest.mark.asyncio +async def test_first_run_for_a_key_has_nothing_to_seed(tmp_path): + session = MemorySession(FakeStore(), "sid") + assert await session.seed(tmp_path) is False + assert session.resumed is False + + +@pytest.mark.asyncio +async def test_persist_of_an_empty_session_writes_nothing(tmp_path): + store = FakeStore() + await MemorySession(store, "sid").persist(tmp_path) + assert store.data == {} + assert store.saved == 0 + + +@pytest.mark.asyncio +async def test_memory_failures_never_break_the_run(tmp_path): + class BrokenStore(FakeStore): + async def _read_json(self, path, default=None): + raise RuntimeError("store unavailable") + + async def _write_json(self, path, value, actor=None): + raise RuntimeError("store unavailable") + + (tmp_path / "s.jsonl").write_text("{}\n") + session = MemorySession(BrokenStore(), "sid") + assert await session.seed(tmp_path) is False + await session.persist(tmp_path) # must not raise + + +@pytest.mark.asyncio +async def test_wire_uses_the_key_derived_session_id(tmp_path): + store = FakeStore() + with patch("flyteplugins.agents.deepseek._memory.resolve_memory", return_value=store): + session = await wire_memory_session(tmp_path, memory_key="user-1") + assert session is not None + assert session.session_id == memory_session_id("user-1") + + +@pytest.mark.asyncio +async def test_wire_is_noop_without_a_key(tmp_path): + assert await wire_memory_session(tmp_path, memory_key=None) is None + + +@pytest.mark.asyncio +async def test_wire_is_noop_when_no_store_is_available(tmp_path): + """No Flyte context/org: the run proceeds without cross-run memory.""" + with patch("flyteplugins.agents.deepseek._memory.resolve_memory", return_value=None): + assert await wire_memory_session(tmp_path, memory_key="user-1") is None diff --git a/plugins/agents/deepseek/tests/test_deepseek_run.py b/plugins/agents/deepseek/tests/test_deepseek_run.py new file mode 100644 index 000000000..5dbc825f9 --- /dev/null +++ b/plugins/agents/deepseek/tests/test_deepseek_run.py @@ -0,0 +1,378 @@ +"""Tests for ``run_agent`` — config layering, prompt composition, sessions, cleanup. + +Offline: ``FakeHarness`` stands in for ``DeepSeekHarness``, capturing the config +it was built with and the prompt it was driven with, and returning a canned +``RunResult``. One test has the fake harness actually execute a published tool +shim, which is the real contract: tool calls must be served while the harness's +blocking ``run`` is in flight. +""" + +import asyncio +import json +import pathlib +import subprocess +from types import SimpleNamespace +from unittest.mock import AsyncMock, patch + +import flyte +import pytest +from deepseek_harness import DeepSeekHarnessConfig, Notification, RunResult + +from flyteplugins.agents.deepseek import TOOLS_DIRNAME, run_agent, run_agent_sync, tool + +CALLS = [] + + +class FakeHarness: + """Mimics DeepSeekHarness: a context manager with a blocking ``run``.""" + + # Set by a test to run extra work inside ``run`` (e.g. call a tool shim). + during_run = None + # Notifications the fake streams back to ``on_notification``. + stream = () + result_text = "the answer" + finish_reason = "completed" + + def __init__(self, config): + self.config = config + CALLS.append(self) + + def __enter__(self): + return self + + def __exit__(self, *_): + return None + + def run(self, prompt, session_id=None, on_notification=None): + self.prompt = prompt + self.session_id = session_id + for notification in type(self).stream: + on_notification(notification) + extra = type(self).during_run + self.during_run_result = extra(self) if extra else None + return RunResult( + session_id=session_id or "generated", + final_response=type(self).result_text, + finish_reason=type(self).finish_reason, + events=[{"type": "assistant/message"}], + notifications=[], + ) + + +@pytest.fixture(autouse=True) +def _reset_fake(): + CALLS.clear() + FakeHarness.during_run = None + FakeHarness.stream = () + FakeHarness.result_text = "the answer" + FakeHarness.finish_reason = "completed" + yield + FakeHarness.during_run = None + + +def _patch_harness(): + return patch("flyteplugins.agents.deepseek._run.DeepSeekHarness", FakeHarness) + + +def _weather_tool(env_name): + env = flyte.TaskEnvironment(env_name) + + @tool + @env.task + async def get_weather(city: str) -> str: + """Get the current weather for a city.""" + return f"sunny in {city}" + + return get_weather + + +@pytest.mark.asyncio +async def test_returns_the_final_response(): + with _patch_harness(): + assert await run_agent("hello", durable=False, observability=False) == "the answer" + + +@pytest.mark.asyncio +async def test_adapter_owns_workspace_and_session_root(): + """The workspace is where shims are published; the session root is what we mirror.""" + with _patch_harness(): + await run_agent("hello", durable=False, observability=False) + + cfg = CALLS[0].config + assert isinstance(cfg, DeepSeekHarnessConfig) + assert pathlib.Path(cfg.cwd).is_absolute() + assert cfg.session_root is not None + + +@pytest.mark.asyncio +async def test_model_provider_and_max_tokens_are_layered_on(): + with _patch_harness(): + await run_agent( + "hello", + model="deepseek-v4-flash", + provider="deepseek-official", + max_tokens=1024, + durable=False, + observability=False, + ) + + cfg = CALLS[0].config + assert (cfg.model, cfg.provider, cfg.max_tokens) == ("deepseek-v4-flash", "deepseek-official", 1024) + + +@pytest.mark.asyncio +async def test_a_caller_supplied_config_is_preserved(): + """SDK-native configuration survives; only cwd/session_root are the adapter's.""" + base = DeepSeekHarnessConfig(cordis="/tmp/custom.cordis.yml", base_url="https://proxy.example", model="base-model") + with _patch_harness(): + await run_agent("hello", config=base, model="override-model", durable=False, observability=False) + + cfg = CALLS[0].config + assert cfg.cordis == "/tmp/custom.cordis.yml" + assert cfg.base_url == "https://proxy.example" + assert cfg.model == "override-model" + + +@pytest.mark.asyncio +async def test_extra_kwargs_reach_the_sdk_config(): + with _patch_harness(): + await run_agent("hello", api_key="sk-test", request_timeout_seconds=12.0, durable=False, observability=False) + + cfg = CALLS[0].config + assert cfg.api_key == "sk-test" + assert cfg.request_timeout_seconds == 12.0 + + +@pytest.mark.asyncio +async def test_instructions_become_the_runtime_system_prompt(): + with _patch_harness(): + await run_agent("hello", instructions="Be concise.", durable=False, observability=False) + + assert CALLS[0].config.env["DSH_SYSTEM_PROMPT"] == "Be concise." + + +@pytest.mark.asyncio +async def test_prompt_is_untouched_when_there_are_no_tools(): + with _patch_harness(): + await run_agent("what is 2+2?", durable=False, observability=False) + + assert CALLS[0].prompt == "what is 2+2?" + + +@pytest.mark.asyncio +async def test_prompt_carries_the_tool_manual(): + """The harness has no tool-declaration channel, so the manual rides the prompt.""" + with _patch_harness(): + await run_agent("weather in Paris?", tools=[_weather_tool("ds_run_a")], durable=False, observability=False) + + prompt = CALLS[0].prompt + assert "get_weather(city: string)" in prompt + assert f"{TOOLS_DIRNAME}/get_weather" in prompt + assert prompt.endswith("weather in Paris?") + + +@pytest.mark.asyncio +async def test_bare_tasks_are_accepted_as_tools(): + env = flyte.TaskEnvironment("ds_run_b") + + @env.task + async def get_population(city: str) -> int: + """Get the population of a city.""" + return 1 + + with _patch_harness(): + await run_agent("hello", tools=[get_population], durable=False, observability=False) + + assert "get_population(city: string)" in CALLS[0].prompt + + +@pytest.mark.asyncio +async def test_tools_are_callable_while_the_blocking_run_is_in_flight(): + """The core of the design: ``run`` blocks a worker thread, the loop serves tools.""" + weather = _weather_tool("ds_run_c") + + def call_the_shim(harness): + shim = pathlib.Path(harness.config.cwd) / TOOLS_DIRNAME / "get_weather" + return subprocess.run( + [str(shim), json.dumps({"city": "Paris"})], capture_output=True, text=True, timeout=30, check=False + ) + + FakeHarness.during_run = call_the_shim + with ( + _patch_harness(), + patch.object(weather.task, "aio", new_callable=AsyncMock, return_value="sunny in Paris") as mock_aio, + ): + await run_agent("weather?", tools=[weather], durable=False, observability=False) + + done = CALLS[0].during_run_result + assert done.returncode == 0, done.stderr + assert done.stdout.strip() == "sunny in Paris" + mock_aio.assert_awaited_once_with(city="Paris") + + +@pytest.mark.asyncio +async def test_durable_session_id_drives_the_run(tmp_path): + session = SimpleNamespace(session_id="flyte-abc", resumed=False, persist=AsyncMock()) + with ( + _patch_harness(), + patch("flyteplugins.agents.deepseek._run.wire_durable_session", AsyncMock(return_value=session)), + ): + await run_agent("hello", observability=False) + + assert CALLS[0].session_id == "flyte-abc" + session.persist.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_memory_takes_precedence_over_the_per_run_checkpoint(): + """A keyed store survives retries too, so it subsumes crash-resume.""" + memory = SimpleNamespace(session_id="flyte-mem-1", resumed=True, persist=AsyncMock()) + wire_durable = AsyncMock() + with ( + _patch_harness(), + patch("flyteplugins.agents.deepseek._run.wire_memory_session", AsyncMock(return_value=memory)), + patch("flyteplugins.agents.deepseek._run.wire_durable_session", wire_durable), + ): + await run_agent("hello", memory_key="user-1", observability=False) + + assert CALLS[0].session_id == "flyte-mem-1" + wire_durable.assert_not_awaited() + memory.persist.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_an_explicit_session_id_wins(): + with _patch_harness(): + await run_agent("hello", session_id="mine", durable=False, observability=False) + + assert CALLS[0].session_id == "mine" + + +@pytest.mark.asyncio +async def test_the_temp_workspace_is_removed_after_the_run(): + with _patch_harness(): + await run_agent("hello", tools=[_weather_tool("ds_run_d")], durable=False, observability=False) + + assert not pathlib.Path(CALLS[0].config.cwd).exists() + assert not pathlib.Path(CALLS[0].config.session_root).exists() + + +@pytest.mark.asyncio +async def test_a_caller_workspace_is_kept_minus_the_shims(tmp_path): + """Bring your own workspace (e.g. a downloaded Dir): we leave it as we found it.""" + (tmp_path / "repo.txt").write_text("hello") + with _patch_harness(): + await run_agent( + "hello", tools=[_weather_tool("ds_run_e")], workspace=tmp_path, durable=False, observability=False + ) + + assert (tmp_path / "repo.txt").read_text() == "hello" + assert not (tmp_path / TOOLS_DIRNAME).exists() + + +@pytest.mark.asyncio +async def test_session_events_are_rendered_to_the_report(): + FakeHarness.stream = ( + Notification( + method="session.event", + payload={ + "sessionId": "s", + "event": { + "type": "assistant/message", + "data": {"message": {"content": [{"type": "text", "text": "thinking out loud"}]}}, + }, + }, + ), + Notification( + method="session.event", + payload={"sessionId": "s", "event": {"type": "turn/end", "data": {"reason": {"kind": "completed"}}}}, + ), + Notification(method="session.status", payload={"sessionId": "s", "status": "idle"}), + ) + rows = [] + + class RecordingTimeline: + def heading(self, _text): + pass + + def row(self, **kwargs): + rows.append(kwargs) + + with _patch_harness(), patch("flyteplugins.agents.deepseek._run.ReportTimeline", RecordingTimeline): + await run_agent("hello", durable=False, observability=True) + await asyncio.sleep(0.05) # let the marshalled render callbacks land + + labels = [r["label"] for r in rows] + assert "assistant" in labels + assert "turn" in labels + assert labels[-1] == "result" + assert "thinking out loud" in next(r["detail"] for r in rows if r["label"] == "assistant") + + +def test_harness_tool_events_are_rendered(): + """The runtime's own tools (bash, editor) surface as tool/call + tool/result.""" + from flyteplugins.agents.deepseek._run import _render_event + + rows = [] + + class RecordingTimeline: + def row(self, **kwargs): + rows.append(kwargs) + + timeline = RecordingTimeline() + _render_event( + timeline, + {"type": "tool/call", "data": {"name": "bash", "arguments": '{"command": "ls"}', "callId": "c1"}}, + ) + _render_event(timeline, {"type": "tool/result", "data": {"message": "stats.py test_stats.py"}}) + _render_event( + timeline, + {"type": "tool/result", "data": {"error": {"name": "bash", "code": "ENOENT"}, "message": "not found"}}, + ) + + assert rows[0]["label"] == "bash" + assert "ls" in rows[0]["detail"] + assert rows[1]["meta"] == "harness tool result" + assert rows[1]["error"] is None + assert rows[2]["error"] == "error" + + +def test_assistant_usage_is_summarized(): + from flyteplugins.agents.deepseek._run import _render_event + + rows = [] + + class RecordingTimeline: + def row(self, **kwargs): + rows.append(kwargs) + + _render_event( + RecordingTimeline(), + { + "type": "assistant/message", + "data": { + "message": {"content": [{"type": "text", "text": "hi"}]}, + "usage": {"inputTokens": 5000, "outputTokens": 120}, + }, + }, + ) + assert rows[0]["meta"] == "in 5.0k · out 120" + + +def test_a_malformed_event_never_breaks_the_run(): + """Observability is best-effort: junk on the wire must not raise.""" + from flyteplugins.agents.deepseek._run import _render_event + + class BrokenTimeline: + def row(self, **kwargs): + raise RuntimeError("report backend down") + + _render_event(BrokenTimeline(), {"type": "assistant/message", "data": {"message": {"content": [{"text": "x"}]}}}) + _render_event(BrokenTimeline(), {"type": "turn/end", "data": None}) + _render_event(BrokenTimeline(), {}) + + +def test_run_agent_sync_drives_the_same_path(): + """Sync tasks call the sync companion; it runs the async body on its own loop.""" + with _patch_harness(): + assert run_agent_sync("hello", durable=False, observability=False) == "the answer" diff --git a/plugins/agents/deepseek/tests/test_deepseek_tools.py b/plugins/agents/deepseek/tests/test_deepseek_tools.py new file mode 100644 index 000000000..9f845b326 --- /dev/null +++ b/plugins/agents/deepseek/tests/test_deepseek_tools.py @@ -0,0 +1,140 @@ +"""Unit tests for the DeepSeek adapter's tool wrapper (no network / no controller).""" + +import re +from unittest.mock import AsyncMock, patch + +import flyte +import pytest +from flyteplugins.agents.core import ToolTaskResolver + +from flyteplugins.agents.deepseek import HarnessTool, tool + + +def test_task_becomes_harness_tool_with_resolver(): + env = flyte.TaskEnvironment("ds_tools_a") + + @tool + @env.task + def get_weather(city: str) -> str: + """Get the current weather for a city.""" + return f"sunny in {city}" + + assert isinstance(get_weather, HarnessTool) + assert get_weather.name == "get_weather" + assert get_weather.description == "Get the current weather for a city." + # The tool shadows the task at module scope, so the real task stays reachable + # and resolves to itself on the worker instead of re-dispatching the tool. + assert get_weather.__wrapped_task__ is get_weather.task + assert isinstance(get_weather.task.task_resolver, ToolTaskResolver) + + +def test_schema_comes_from_the_flyte_type_engine(): + env = flyte.TaskEnvironment("ds_tools_b") + + @tool + @env.task + def book(city: str, nights: int, budget: float, refundable: bool = True) -> str: + """Book a stay.""" + return city + + properties = book.schema["properties"] + assert properties["city"]["type"] == "string" + assert properties["nights"]["type"] == "integer" + assert properties["budget"]["type"] == "number" + assert properties["refundable"]["type"] == "boolean" + + +def test_name_and_description_overrides(): + env = flyte.TaskEnvironment("ds_tools_c") + + @tool(name="weather", description="Weather lookup.") + @env.task + def get_weather(city: str) -> str: + """Original docstring.""" + return city + + assert get_weather.name == "weather" + assert get_weather.description == "Weather lookup." + + +def test_usage_line_names_params_and_marks_optionals(): + env = flyte.TaskEnvironment("ds_tools_d") + + @tool + @env.task + def search(query: str, limit: int = 5) -> str: + """Search the index.""" + return query + + usage = search.usage() + assert usage.startswith("search(") + assert "query: string" in usage + assert "limit: integer (optional)" in usage + assert usage.endswith("— Search the index.") + + +@pytest.mark.asyncio +async def test_invoke_dispatches_to_task_aio(): + """``invoke`` goes through ``task.aio`` — the durable child-action seam.""" + env = flyte.TaskEnvironment("ds_tools_e") + + @tool + @env.task + async def get_weather(city: str) -> str: + """Get the current weather for a city.""" + return f"sunny in {city}" + + with patch.object(get_weather.task, "aio", new_callable=AsyncMock, return_value="sunny in Paris") as mock_aio: + assert await get_weather.invoke({"city": "Paris"}) == "sunny in Paris" + + mock_aio.assert_awaited_once_with(city="Paris") + + +@pytest.mark.asyncio +async def test_invoke_coerces_llm_ints_to_floats(): + """An LLM emits ``42`` for a float param; the type engine would reject it.""" + env = flyte.TaskEnvironment("ds_tools_f") + + @tool + @env.task + async def convert(amount_usd: float) -> float: + """Convert dollars to euros.""" + return amount_usd * 0.9 + + with patch.object(convert.task, "aio", new_callable=AsyncMock, return_value=37.8) as mock_aio: + assert await convert.invoke({"amount_usd": 42}) == "37.8" + + mock_aio.assert_awaited_once_with(amount_usd=42.0) + assert isinstance(mock_aio.await_args.kwargs["amount_usd"], float) + + +@pytest.mark.asyncio +async def test_non_string_results_are_json_encoded(): + env = flyte.TaskEnvironment("ds_tools_g") + + @tool + @env.task + async def stats(city: str) -> dict: + """Get city stats.""" + return {"city": city, "population": 2102650} + + payload = {"city": "Paris", "population": 2102650} + with patch.object(stats.task, "aio", new_callable=AsyncMock, return_value=payload): + assert await stats.invoke({"city": "Paris"}) == '{"city": "Paris", "population": 2102650}' + + +@pytest.mark.asyncio +async def test_plain_callable_becomes_a_tool(): + def add(a: int, b: int) -> int: + """Add two numbers.""" + return a + b + + add_tool = tool(add) + assert isinstance(add_tool, HarnessTool) + assert add_tool.name == "add" + assert await add_tool.invoke({"a": 2, "b": 3}) == "5" + + +def test_tool_rejects_non_callables(): + with pytest.raises(TypeError, match=re.escape("expects a Flyte @env.task or a callable")): + tool(42) diff --git a/plugins/agents/deepseek/uv.lock b/plugins/agents/deepseek/uv.lock new file mode 100644 index 000000000..505ae7e1f --- /dev/null +++ b/plugins/agents/deepseek/uv.lock @@ -0,0 +1,1513 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" + +[options] +prerelease-mode = "allow" + +[[package]] +name = "aiofiles" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, +] + +[[package]] +name = "aiolimiter" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/23/b52debf471f7a1e42e362d959a3982bdcb4fe13a5d46e63d28868807a79c/aiolimiter-1.2.1.tar.gz", hash = "sha256:e02a37ea1a855d9e832252a105420ad4d15011505512a1a1d814647451b5cca9", size = 7185, upload-time = "2024-12-08T15:31:51.496Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/ba/df6e8e1045aebc4778d19b8a3a9bc1808adb1619ba94ca354d9ba17d86c3/aiolimiter-1.2.1-py3-none-any.whl", hash = "sha256:d3f249e9059a20badcb56b61601a83556133655c11d1eb3dd3e04ff069e5f3c7", size = 6711, upload-time = "2024-12-08T15:31:49.874Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, +] + +[[package]] +name = "anyio" +version = "4.14.2" +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/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, +] + +[[package]] +name = "async-lru" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/1f/989ecfef8e64109a489fff357450cb73fa73a865a92bd8c272170a6922c2/async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6", size = 16332, upload-time = "2026-03-19T01:04:32.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315", size = 8403, upload-time = "2026-03-19T01:04:30.883Z" }, +] + +[[package]] +name = "asyncssh" +version = "2.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/7f/2d79247bacc562104f312d27efe541673aa177feac89de291bd61bca52be/asyncssh-2.24.0.tar.gz", hash = "sha256:4064c590e59ce2e8d82a2f66d35f3120d765828b4df5e3dbfb07b4a8c24686c9", size = 550148, upload-time = "2026-06-27T20:34:44.755Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", hash = "sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", size = 381237, upload-time = "2026-06-27T20:34:43.198Z" }, +] + +[[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 = "certifi" +version = "2026.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "cffi" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" }, + { url = "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" }, + { url = "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" }, + { url = "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" }, + { url = "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" }, + { url = "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" }, + { url = "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" }, + { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" }, + { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" }, + { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" }, + { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" }, + { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, + { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, + { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" }, + { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" }, + { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" }, + { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" }, + { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" }, + { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" }, + { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" }, + { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" }, + { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" }, + { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" }, + { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" }, + { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" }, + { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" }, + { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" }, + { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" }, + { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" }, + { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + +[[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 = "connectrpc" +version = "0.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, + { name = "pyqwest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/11/adb3ab104282202000b1c98213c70c7068d76917d9f17c83b818e3d212c1/connectrpc-0.10.1.tar.gz", hash = "sha256:eaf093a4f0d1b9c854c71839083ed5242a551100e99cf52e3cc5e35663f2a5ea", size = 47043, upload-time = "2026-05-29T02:19:57.2Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/67/49a6a2d1d0ee04b4d96410d12c751a44437a9bdf4991d7b88fa5c231c0d2/connectrpc-0.10.1-py3-none-any.whl", hash = "sha256:6e965625bbc4b185532bd98e4919528bea6d1c24619b6dba416d0dfafc10e344", size = 64120, upload-time = "2026-05-29T02:19:55.991Z" }, +] + +[[package]] +name = "cryptography" +version = "50.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/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/5c/59086b4aac5e879d38ddbcf74e4be7ade89cebc3eb199a55da998c3bb46a/cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03", size = 4001252, upload-time = "2026-07-31T14:23:33.331Z" }, + { url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" }, + { url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" }, + { url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" }, + { url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" }, + { url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" }, + { url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" }, + { url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" }, + { url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" }, + { url = "https://files.pythonhosted.org/packages/32/2e/c9db68a0c4bfa28e310707527c0ee3a2bd254104d2e02e68f368e197aa4c/cryptography-50.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", size = 3840395, upload-time = "2026-07-31T14:23:56.677Z" }, + { url = "https://files.pythonhosted.org/packages/c3/fb/951032a3bf22a5697c83183fb6294a4843772947a70e616c57b3ff5f522e/cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41", size = 3989258, upload-time = "2026-07-31T14:23:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" }, + { url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" }, + { url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" }, + { url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" }, + { url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" }, + { url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" }, + { url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" }, + { url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" }, + { url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" }, + { url = "https://files.pythonhosted.org/packages/74/9a/02ffe35b2853d121689871eb5dce862092562b3a1ed5cc98f1aaed441506/cryptography-50.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269", size = 3816291, upload-time = "2026-07-31T14:24:22.125Z" }, + { url = "https://files.pythonhosted.org/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07", size = 4036009, upload-time = "2026-07-31T14:24:24.122Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" }, + { url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" }, + { url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" }, + { url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" }, + { url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" }, + { url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" }, + { url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" }, + { url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" }, + { url = "https://files.pythonhosted.org/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba", size = 3874135, upload-time = "2026-07-31T14:24:50.085Z" }, + { url = "https://files.pythonhosted.org/packages/9d/3e/e54cde8c01631a5a8226ccd617eab9e57fd5cfdad90f1a9e6bb570794631/cryptography-50.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c", size = 3963170, upload-time = "2026-07-31T14:24:51.968Z" }, + { url = "https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a", size = 4692441, upload-time = "2026-07-31T14:24:53.743Z" }, + { url = "https://files.pythonhosted.org/packages/53/c9/a5151588710785a96d7bc4de27d4cd62f263bbbcb203cfe29df537eb6505/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e", size = 4699810, upload-time = "2026-07-31T14:24:55.746Z" }, + { url = "https://files.pythonhosted.org/packages/c7/1a/15b92b25eb6ce3089cd49377ae990a0f3ad485a510f968aed1f19dbdcdf2/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d", size = 4691924, upload-time = "2026-07-31T14:24:58.082Z" }, + { url = "https://files.pythonhosted.org/packages/62/15/219075012ab13e8905f3cd572204f4acb4b111df787104346b9bc0cea789/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437", size = 4699593, upload-time = "2026-07-31T14:24:59.951Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b5/c2c5fce26f0ee40d21bafe7f191d29a34b35a65ac4fe8a1191d1983612e9/cryptography-50.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9", size = 3813796, upload-time = "2026-07-31T14:25:02.298Z" }, +] + +[[package]] +name = "deepseek-harness-runtime-bin" +version = "0.1.0rc7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/6e/c7d6992c148312a9abee04115802a44eb474d9dd5464cc6856b14518bc8a/deepseek_harness_runtime_bin-0.1.0rc7-py3-none-macosx_14_0_arm64.whl", hash = "sha256:39ae51dec905c496ede69250d51b02a424a6c96829feb2ad7e01b3cec97a3255", size = 52067269, upload-time = "2026-08-18T02:04:29.222Z" }, + { url = "https://files.pythonhosted.org/packages/10/64/86796e0394b577741850c6818e8dad8e13afc38c9b4692c7b4738045916f/deepseek_harness_runtime_bin-0.1.0rc7-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:de4e2571d3cd1c521ee22c754460dd251b45bd91a033876f554d0eab9b2843a1", size = 56534076, upload-time = "2026-08-18T02:04:32.529Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f1/91501f0f08949fbc592f790a1a1a327ff781fe35138765eac38afb77c433/deepseek_harness_runtime_bin-0.1.0rc7-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:bf26f76a8cab7bedcce3a060b81d19ec1cabd8bd261c40b47381d630742797a7", size = 56796555, upload-time = "2026-08-18T02:04:35.677Z" }, +] + +[[package]] +name = "deepseek-harness-sdk" +version = "0.1.0rc7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deepseek-harness-runtime-bin" }, + { name = "pydantic" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/5e/86d118a15ad0f5b42933fb7162420b60b7cc53a4739da272a5204501e009/deepseek_harness_sdk-0.1.0rc7-py3-none-any.whl", hash = "sha256:5327d60659d8802442d2c589c89c3528cf18eb07b5d698059c833eb6b853b7d4", size = 12881, upload-time = "2026-08-18T02:05:09.933Z" }, +] + +[[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 = "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 = "flyte" +source = { editable = "../../../" } +dependencies = [ + { name = "aiofiles" }, + { name = "aiolimiter" }, + { name = "async-lru" }, + { name = "asyncssh" }, + { name = "click" }, + { name = "cloudpickle" }, + { name = "connectrpc" }, + { name = "docstring-parser" }, + { name = "flyteidl2" }, + { name = "fsspec" }, + { name = "httpx" }, + { name = "keyring" }, + { name = "mashumaro" }, + { name = "msgpack" }, + { name = "obstore" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "pyopenssl" }, + { name = "pyyaml" }, + { name = "rich-click" }, + { name = "sentry-sdk" }, + { name = "toml" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiofiles", specifier = ">=24.1.0" }, + { name = "aiolimiter", specifier = ">=1.2.1" }, + { name = "aiosqlite", marker = "extra == 'aiosqlite'", specifier = ">=0.21.0" }, + { name = "async-lru", specifier = ">=2.0.5" }, + { name = "asyncssh", specifier = ">=2.14" }, + { name = "click", specifier = ">=8.2.1" }, + { name = "cloudpickle", specifier = ">=3.1.1" }, + { name = "connectrpc", specifier = ">=0.9.0,<0.11" }, + { name = "deltalake", marker = "extra == 'examples-test'" }, + { name = "docstring-parser", specifier = ">=0.16" }, + { name = "fastapi", marker = "extra == 'examples-test'" }, + { name = "flyte-controller-base", marker = "extra == 'rust-controller'", directory = "../../../rs_controller" }, + { name = "flyteidl2", specifier = "==2.0.42" }, + { name = "fsspec", specifier = ">=2025.3.0" }, + { name = "grpcio", marker = "extra == 'connector'", specifier = ">=1.71.0" }, + { name = "grpcio-health-checking", marker = "extra == 'connector'" }, + { name = "httpx", specifier = ">=0.28.1,<1.0.0" }, + { name = "httpx", marker = "extra == 'connector'" }, + { name = "joblib", marker = "extra == 'examples-test'" }, + { name = "keyring", specifier = ">=25.6.0" }, + { name = "lightning", marker = "extra == 'examples-test'" }, + { name = "mashumaro", specifier = ">=3.15" }, + { name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.26.0,<2" }, + { name = "msgpack", specifier = ">=1.1.0" }, + { name = "nest-asyncio", marker = "extra == 'examples-test'" }, + { name = "obstore", specifier = ">=0.7.3" }, + { name = "packaging" }, + { name = "pandas", marker = "extra == 'examples-test'" }, + { name = "polyglot-hello", marker = "extra == 'examples-test'", specifier = ">=0.1.3" }, + { name = "prometheus-client", marker = "extra == 'connector'" }, + { name = "protobuf", specifier = ">=6.30.1" }, + { name = "pyarrow", marker = "extra == 'examples-test'" }, + { name = "pydantic", specifier = ">=2.10.6" }, + { name = "pydantic-monty", marker = "extra == 'sandbox'", specifier = "==0.0.17" }, + { name = "pyopenssl", specifier = ">=24.0.0" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "regex", marker = "extra == 'mcp'", specifier = ">=2024.5.15" }, + { name = "rich-click", specifier = "==1.8.9" }, + { name = "scikit-learn", marker = "extra == 'examples-test'" }, + { name = "sentry-sdk", specifier = ">=2.0" }, + { name = "starlette", marker = "extra == 'mcp'" }, + { name = "textual", marker = "extra == 'tui'", specifier = ">=0.80" }, + { name = "toml", specifier = ">=0.10.2" }, + { name = "uvicorn", marker = "extra == 'examples-test'" }, + { name = "uvicorn", marker = "extra == 'mcp'" }, +] +provides-extras = ["aiosqlite", "connector", "examples-test", "sandbox", "mcp", "tui", "rust-controller"] + +[package.metadata.requires-dev] +dev = [ + { name = "build", specifier = ">=1.2.2.post1" }, + { name = "docstring-parser" }, + { name = "fastapi", specifier = ">=0.128.0" }, + { name = "google-cloud-bigquery", specifier = ">=3.31.0" }, + { name = "ipywidgets", specifier = ">=8.1.7" }, + { name = "jupyterlab", specifier = ">=4.4.3" }, + { name = "kubernetes" }, + { name = "mcp", specifier = ">=1.26.0,<2" }, + { name = "mock", specifier = ">=5.2.0" }, + { name = "mypy", specifier = ">=1.16.0" }, + { name = "orjson" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pydantic-monty", specifier = "==0.0.17" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-asyncio", specifier = ">=0.26.0" }, + { name = "pytest-benchmark", specifier = ">=5.1.0" }, + { name = "pytest-xdist" }, + { name = "regex", specifier = ">=2024.5.15" }, + { name = "ruff", specifier = ">=0.11.0" }, + { name = "setuptools-scm", specifier = ">=8.2.0" }, + { name = "starlette" }, + { name = "textual", specifier = ">=0.80" }, + { name = "ty", specifier = ">=0.0.59" }, + { name = "types-aiofiles" }, + { name = "types-pyyaml" }, + { name = "uvicorn" }, + { name = "uvicorn", specifier = ">=0.40.0" }, +] + +[[package]] +name = "flyteidl2" +version = "2.0.42" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "protobuf" }, + { name = "protoc-gen-openapiv2" }, + { name = "protovalidate" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/26/d493ef6429896c89a37ec0192cbabd07119e8be1193262092db020b310f1/flyteidl2-2.0.42-py3-none-any.whl", hash = "sha256:ebe9f59447a4061908f0e1bc14927222604e2968b36e9f25a7915968c8be594d", size = 376628, upload-time = "2026-08-15T00:06:47.872Z" }, +] + +[[package]] +name = "flyteplugins-agents-core" +source = { editable = "../core" } +dependencies = [ + { name = "flyte" }, +] + +[package.metadata] +requires-dist = [{ name = "flyte", editable = "../../../" }] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-asyncio", specifier = ">=0.26.0" }, +] + +[[package]] +name = "flyteplugins-agents-deepseek" +source = { editable = "." } +dependencies = [ + { name = "deepseek-harness-sdk" }, + { name = "flyteplugins-agents-core" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, +] + +[package.metadata] +requires-dist = [ + { name = "deepseek-harness-sdk", specifier = ">=0.1.0rc7" }, + { name = "flyteplugins-agents-core", editable = "../core" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-asyncio", specifier = ">=0.26.0" }, +] + +[[package]] +name = "fsspec" +version = "2026.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/78/f34251dadb8f3921264a1d9b8946f5e542014ee2614b285261b4e40e6775/fsspec-2026.7.0.tar.gz", hash = "sha256:c803c40f4cf860b49dea58ee3e1c33cb9c790520e233537e1340049f89b82a88", size = 317040, upload-time = "2026-07-28T16:34:51.052Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/3c/6a2bf344106328fd04963664a60b9bb6496fc25df8e962fcdc1367285fb9/fsspec-2026.7.0-py3-none-any.whl", hash = "sha256:b57ddbafedfaef7018c1ecab32aa200a9d7ca26b77965f64e48b70061249d279", size = 206583, upload-time = "2026-07-28T16:34:49.538Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.75.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/73/74bcab964c9a7a61f2bb71e8179b0f13e6fa98f7ce00fd168aab291e4a2e/googleapis_common_protos-1.75.1.tar.gz", hash = "sha256:d3042c6c5a2d4e67113104d6b6818b59b6bd92a197f2a91508e801fe815cf071", size = 150967, upload-time = "2026-08-06T06:24:51.972Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/51/186c02b8549b69ccda44429cf6ff5081e4b61a602ddfe6a8020d1be31d1b/googleapis_common_protos-1.75.1-py3-none-any.whl", hash = "sha256:28a1934bcd33b9c9da66ac301a0a4227e3367f095a17d0375cb98f0a09d93b79", size = 300626, upload-time = "2026-08-06T06:23:46.696Z" }, +] + +[[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 = "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 = "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 = "idna" +version = "3.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789, upload-time = "2026-03-20T06:42:55.665Z" }, +] + +[[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.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/1f/c23395957d41ccf27c4e535c3d334c4051e5395b3752057ba4cbaec35c56/jaraco_functools-4.6.0.tar.gz", hash = "sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280", size = 20837, upload-time = "2026-07-14T01:28:02.544Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl", hash = "sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30", size = 11677, upload-time = "2026-07-14T01:28:01.59Z" }, +] + +[[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 = "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 = "mashumaro" +version = "3.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/e3/a06dcd2e6df094c5e294721926f1f76da30f50823e2ac233a9198e804891/mashumaro-3.22.tar.gz", hash = "sha256:64538cc365204402a060ebde683a86505b5a4344acf6870d79021e9fbfe57360", size = 197845, upload-time = "2026-05-26T14:39:21.859Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/1c/92fd926c2e7763535454683250dbdd8d10aa2f2c62f58d6abbcce4d8b3fc/mashumaro-3.22-py3-none-any.whl", hash = "sha256:17dc4d7294c33ef380a8b929dda0608577aa2141988c00a0c4932310108fe71d", size = 95916, upload-time = "2026-05-26T14:39:20.233Z" }, +] + +[[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.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 = "msgpack" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/f9/c0a1c127f9049db9155afc316952ea571720dd01833ff5e4d7e8e6352dbb/msgpack-1.2.1.tar.gz", hash = "sha256:04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647", size = 183960, upload-time = "2026-06-18T16:13:52.594Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/16/f70100614b69feb3ade7285f08c9c52d6cda0a5c03f3f5e2facd63acb211/msgpack-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c7b398c56ff125feae96c2737abfec5595f1fa0aa186df60c56040b8accb95c", size = 82926, upload-time = "2026-06-18T16:12:31.531Z" }, + { url = "https://files.pythonhosted.org/packages/e4/3c/08ecd5cdfe4e2de43aec79062028ad0f7b2d9b1fea5430068c198ba570da/msgpack-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1548006a91aa93c5da81f3bdcebc1a0d10cea2d25969754fbe848da622b2b895", size = 82730, upload-time = "2026-06-18T16:12:32.894Z" }, + { url = "https://files.pythonhosted.org/packages/19/9f/a70c9cb1a04ecc134005149367dcfe35d167284e8f65035a1e4156ad17b5/msgpack-1.2.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1dabedcd0f23559f3596428c6589c1cd8c6eaed3a0d720795b07b0225d769203", size = 400729, upload-time = "2026-06-18T16:12:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/fa/7f/5ce020168cf0439041526e95aa068c722c016aee21624e331aeabeee2e8e/msgpack-1.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83efa1c898e0fc5380fc0cabbf75164c52e3b5cbb45973710d75821928380c73", size = 407625, upload-time = "2026-06-18T16:12:35.239Z" }, + { url = "https://files.pythonhosted.org/packages/79/70/fb7668ce0386819303047057aef6fc1da73b584291d9cff82b821744e2ef/msgpack-1.2.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01e2dd6c9b19d333a00282330cc8a73d38d8dabc306dc5b42cd668c3ac82e833", size = 377891, upload-time = "2026-06-18T16:12:36.684Z" }, + { url = "https://files.pythonhosted.org/packages/3d/dc/9ebe654a73c3aed2e40aa6b52e3c2a02b5f53ef0085fa235a45d5b367f87/msgpack-1.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:350cb813d0af6e65d2f7ef0d729f7ff5be5a8bce03665892f43e5883d4ecc1b8", size = 391987, upload-time = "2026-06-18T16:12:37.839Z" }, + { url = "https://files.pythonhosted.org/packages/42/eb/b67cf64218a2fa25e1c671fe1d3dbb06cbeb973e71bc4b822da079862d0b/msgpack-1.2.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ee1d9ed27d0497b848923746cf762ed2e7db24f4be7eec8e5cbe8c766aa707b7", size = 374603, upload-time = "2026-06-18T16:12:39.221Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2e/9ee200cde32fd1a0101b4006202fde554c1860adfb9bf7bff31ea4c08df8/msgpack-1.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:633727297ed063441fd1cda2288865487f33ad14eeb8831afb5f0c396a62cfce", size = 405121, upload-time = "2026-06-18T16:12:40.524Z" }, + { url = "https://files.pythonhosted.org/packages/43/b6/f10117be7ca7a51e8feed699a907b8e663a8cd66e115ae6b4fb30cc7945c/msgpack-1.2.1-cp310-cp310-win32.whl", hash = "sha256:298872ecf9e61950f1c6af4ca969b859ee91783bb920ef6e6172697d0c8aad74", size = 64088, upload-time = "2026-06-18T16:12:41.762Z" }, + { url = "https://files.pythonhosted.org/packages/ba/93/89976c696fb0224662239d952c47b4d1661b34d79a332ef5584facaa8579/msgpack-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2ff164c1b0bcb740b073b99e945234d0212852fa378e44a208c425379140dbeb", size = 70113, upload-time = "2026-06-18T16:12:42.78Z" }, + { url = "https://files.pythonhosted.org/packages/f4/6b/e9b1cdc042c4458801d2545ed782a95f3d6ba8e270cce8745b8603c7f748/msgpack-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:29a3f6e9667868429d8240dfd063ea5ffdc1321c13d783aa23827a38de0dcb22", size = 82812, upload-time = "2026-06-18T16:12:45.022Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3a/dd518a1bf78ed1e9ad8afe57307c079a00eafe4b3068932a27ca1ea56b4f/msgpack-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aded5bdf32609dc7987a49bbbd15a8ef096193f96dd8bbeb791de729e650acf5", size = 82739, upload-time = "2026-06-18T16:12:46.025Z" }, + { url = "https://files.pythonhosted.org/packages/70/e0/7ba9e1542bf0771a27b8b37c1316e3f95ae9d748fd765284655c476ad4ef/msgpack-1.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:146ee4e9ce80b365c6d4c47073da9da7bcec473e58194ceee5dd7620ace77e06", size = 414233, upload-time = "2026-06-18T16:12:47.029Z" }, + { url = "https://files.pythonhosted.org/packages/03/8d/671d81534ea0e2b0e8a121be100020da09eb78861fe3aa8f3ef7dcd3bed1/msgpack-1.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a28d076ca7c82b9c8728ad90b7147489449557038bed50e4241eb832395169b4", size = 423843, upload-time = "2026-06-18T16:12:48.19Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b6/e5c737515ed1f166664b87601b532f58cbb73d8aa6a90b99f7c2c5037e8e/msgpack-1.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7d31c0ac0c640f877804c67cb2bc9f4e23dc2db97e96c2e67fa27d38283b41f8", size = 390772, upload-time = "2026-06-18T16:12:49.624Z" }, + { url = "https://files.pythonhosted.org/packages/a8/46/62ed8c2e87d7021eab19921594d961ef3aa3794eec76c716dc30f3bfd433/msgpack-1.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ff92d7feeaf5bc26c51495b69e2f99ed97ab79346fb6555f44be7dd2ac6503b", size = 409559, upload-time = "2026-06-18T16:12:50.936Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/59aa3887b860bbf43532835e192b1c388a17590d6068ae4f8b2bc74c906e/msgpack-1.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:779197a6513bab3c3632265e3d0f7cb3227e62510841a6f34f1eaa37efbb345e", size = 387838, upload-time = "2026-06-18T16:12:52.161Z" }, + { url = "https://files.pythonhosted.org/packages/09/11/f8563e471093420cf6478cb3271a0175d8402b82d879783d4035d2d03360/msgpack-1.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67f6dd22fa72a93752643f07889796d62739a13415ee630169a8ce764f86cf9f", size = 421732, upload-time = "2026-06-18T16:12:53.556Z" }, + { url = "https://files.pythonhosted.org/packages/57/cf/e673683c4c6c90c1022b24c65af4b03eda72b182a1176ef6449069d66acc/msgpack-1.2.1-cp311-cp311-win32.whl", hash = "sha256:91054a783328e0ea7954b8771095705c8d2243b814743fbaadf14552c9c52c5d", size = 64091, upload-time = "2026-06-18T16:12:54.821Z" }, + { url = "https://files.pythonhosted.org/packages/3f/07/ca212739d179f9083bff2c7c08c24101c3555a334fadc2b876b18768a3ae/msgpack-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2eda0b7ebb1283a98d3e4492ac933c8af6aff59fd3df1c3ed024f536af4b1dc8", size = 70462, upload-time = "2026-06-18T16:12:55.898Z" }, + { url = "https://files.pythonhosted.org/packages/6d/be/6798347b425e26f35db82e69dd83c09716c856a3714e7bffc4c0860fd830/msgpack-1.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ee967f7c7e1df2890c671ff2ee51a28ded0efc95da3e507176dee881ce36c66", size = 65059, upload-time = "2026-06-18T16:12:57.053Z" }, + { url = "https://files.pythonhosted.org/packages/bc/dd/9e8cbd8f5582ca4b590336f2b91ee5662f6a6ca562b565abaf696a0f81ff/msgpack-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ef59c659f289eddf8aa6623823f19fa2f40a4029266889eac7a2505dd210c35", size = 83531, upload-time = "2026-06-18T16:12:58.249Z" }, + { url = "https://files.pythonhosted.org/packages/50/2e/ebdb85a8da151397a2790363676b7ed7c125924fe618e4c6d8befb0cc62c/msgpack-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3567748a5107cb40cdf66a275430c2f87c07777698f4bfd25c35f44d533258c", size = 82657, upload-time = "2026-06-18T16:12:59.396Z" }, + { url = "https://files.pythonhosted.org/packages/26/aa/753ad8b007b464e1d8aa0c8e650b9c5f4f725e658fc5ac8a7635c55b7f6e/msgpack-1.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60926b75d00c8e816ef98f3034f484a8bc64242d66839cef4cf7e503142316a0", size = 410634, upload-time = "2026-06-18T16:13:00.383Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/6adabd4f6d5e686f97dd02ce7fce3fe4cf672cbac36b8f67ff4040e8ad8b/msgpack-1.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:020e881a764b20d8d7ca1a54fc01b8175519d108e3c3f194fddc200bda95951a", size = 419989, upload-time = "2026-06-18T16:13:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cc/85039b7b0eb168aaad7383a23c97e291a11f08351cb45a606ce865e4e3f1/msgpack-1.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4202c74688ca06591f78cb18988228bd4cca2cc75d57b60008372892d2f1e6e6", size = 377544, upload-time = "2026-06-18T16:13:03.637Z" }, + { url = "https://files.pythonhosted.org/packages/ed/bf/35963899493b32030c85fc513b723ae66144ac70c11ebc52e889e16e3d99/msgpack-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8b267ce94efb76fbd1b3373511420074ee3187f0f7811bf394531de13294735a", size = 400842, upload-time = "2026-06-18T16:13:05.012Z" }, + { url = "https://files.pythonhosted.org/packages/a6/df/8e2ac970c8f99264cd9997d1c73df5466bc19da3301d7dc5500862a9b089/msgpack-1.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f1d0f8f98ade9634e01fb704a408f9336c0a8f1117b369f5db83dc7551d8b1", size = 374108, upload-time = "2026-06-18T16:13:06.232Z" }, + { url = "https://files.pythonhosted.org/packages/17/dd/fa8bd265110dfa51c20cb529f9e6d240a16fafe7e645004c6af2d01353ba/msgpack-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f02cf17a6ca1abe29b5f980644f7551f94d71f2011509b26d8625ce038f0df64", size = 414939, upload-time = "2026-06-18T16:13:07.478Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b9/8377a5ad8953fc0437c70cc98d9ae29f27fe5ac5109fbec0812085865735/msgpack-1.2.1-cp312-cp312-win32.whl", hash = "sha256:0c0d9802354507bcba62af19c17918e3eb437cc25e6f50657d511b5856a77aac", size = 64504, upload-time = "2026-06-18T16:13:08.822Z" }, + { url = "https://files.pythonhosted.org/packages/57/7f/ce1e377df7e62461fefd9eb23bfb93a4a523f40a517b377b8f844d836828/msgpack-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c24aa15d5963051e1a5c62b12c50cd705992502b5ec1f3bece6046f33c9fc24", size = 71421, upload-time = "2026-06-18T16:13:09.828Z" }, + { url = "https://files.pythonhosted.org/packages/8f/32/ebfe84c9929f08f188d56c7a2fd913406a9ddad76a634697c1c43b8112e6/msgpack-1.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:4227224aaec8f7fbcbfbd4272319347b2bb4030366502600f8c45588c5187b07", size = 64775, upload-time = "2026-06-18T16:13:11.056Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ac/dcddcab6f6c20ecb387ca5e980371cdb3f87ff69aeca388be97eebc4c074/msgpack-1.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a70e3cf2804a300d921bb0940426e35f4e489a23adfb77a808892241db0a064", size = 83151, upload-time = "2026-06-18T16:13:12.173Z" }, + { url = "https://files.pythonhosted.org/packages/64/71/fbcfa83a1d6a9c6091942d1cfd070962244664b87427a9a49a6897b1b219/msgpack-1.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:491cc39455ca765fad51fb451bf2915eb2cf41192ab5801ce8d67c1d614fe056", size = 82351, upload-time = "2026-06-18T16:13:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/e3/10/ddf7b06db879e8792d13934ddda09ff20bd2a583fd84c9b59aae9b0e650b/msgpack-1.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f310233ef7fb9c14e201c93639fe5f5260b005f56f0b29048e999c30935596cc", size = 407518, upload-time = "2026-06-18T16:13:14.233Z" }, + { url = "https://files.pythonhosted.org/packages/79/d3/36a46a8ed992b781acbc05928bd5bee3c810cb0c3563bf81a7b0c04a1a76/msgpack-1.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:787c9bebb5833e8f6fc8abca3c0597683d8d87f56a8842b6b89c75a5f3176e2d", size = 416405, upload-time = "2026-06-18T16:13:15.435Z" }, + { url = "https://files.pythonhosted.org/packages/f9/84/e8e9598b557c0ba6ddae901a73780a4c75ac667dddf59414b1e56a42fb34/msgpack-1.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc871b997a9370d855b7394465f2f350e847a5b806dd38dcc9c989e7d87da155", size = 376257, upload-time = "2026-06-18T16:13:17.022Z" }, + { url = "https://files.pythonhosted.org/packages/40/16/738fe6d875ad7e2a9429c165322a4ec088f4f273cdfae63d96a89c467961/msgpack-1.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85f57e960d877f2977f6430896191b04a21f8901b3b4baf2e4604329f4db5402", size = 397469, upload-time = "2026-06-18T16:13:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/ca/be/6d5952df75a7f24f35833af764c3a6860780364cb3a0030beb8099e1b2b4/msgpack-1.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1233ee2dd0cefba127583de50ea654677277047d238303521db35def3d7b2e7c", size = 372802, upload-time = "2026-06-18T16:13:19.685Z" }, + { url = "https://files.pythonhosted.org/packages/e1/39/e2ef7dbf0473bcb8dc7c50bf782a892d67414877b63e47fc88eb189ef5e6/msgpack-1.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3dc2feb0876209d9c38aa56cb1de169bd6c4348f1aa48271f241226590993e6", size = 411273, upload-time = "2026-06-18T16:13:21.028Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c5/133f4512a56e983a93445c836c9d94d88f3bc2e0980ff4b9e577bd8416ce/msgpack-1.2.1-cp313-cp313-win32.whl", hash = "sha256:6d09badf350af2be9d189184e04e64cf54ad93569ab3d96fca58bd3e84aad707", size = 64471, upload-time = "2026-06-18T16:13:22.293Z" }, + { url = "https://files.pythonhosted.org/packages/e2/98/577e10b055096a7dd40732358cabaf7180a20c79ed1dcdbb618e4b9deac7/msgpack-1.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:33f14fba63278b714efe6ad07e50ea5f03d91537aa6a1c5f1ceca4cf44013ca9", size = 71274, upload-time = "2026-06-18T16:13:23.455Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ee/0c0048e7cfbef23c6a94791b8959ab28155232e7956de8a305b5ff588f05/msgpack-1.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc5febcd4c99effbc02b528e49d6fd0760b2b7d48c05239e345a5fa6e743d9a", size = 64795, upload-time = "2026-06-18T16:13:24.687Z" }, + { url = "https://files.pythonhosted.org/packages/77/58/cce442852c6b9e1639c7c8ac8fd9143121cb32dab0f308df4d1426a8eb9c/msgpack-1.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:05f340e47e7e47d2da8db9b53e1bb1d294369e9ef45a747441309f6650b8351d", size = 83610, upload-time = "2026-06-18T16:13:25.724Z" }, + { url = "https://files.pythonhosted.org/packages/60/5c/15b4c7a0182f75ffa90751958ba36a9c01cafee367d49a3edc10ed140b01/msgpack-1.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810b916696c86ef0deb3b74588480224df4c1b071136c34183e4a2a4284d7ac7", size = 83138, upload-time = "2026-06-18T16:13:26.781Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a6/99e58722feaffc5f2fbcc0c8c0d1451ab9f84097f7af87291b46af2390f4/msgpack-1.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ca0dacff965c47afdc3749a8469d7302a8f801d6a28758d55120d75e66ce6889", size = 406090, upload-time = "2026-06-18T16:13:28.072Z" }, + { url = "https://files.pythonhosted.org/packages/19/03/8c63e8cf52958534ef688625965ab04c269a6cadd8caef16758b380a821a/msgpack-1.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e2bf9280bceb5efca998435904b5d3e9fdbcc11d90dc9df30aec7973252b720", size = 412106, upload-time = "2026-06-18T16:13:29.427Z" }, + { url = "https://files.pythonhosted.org/packages/63/d2/155d9e71b40e41fd934bc0c48b9b2770f22263e1ac20aad8e29fdca7be3f/msgpack-1.2.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6c4be5d1c02a42b066ca6ddb71adf36432868fdcdb6ee87e634e86e0674190", size = 374851, upload-time = "2026-06-18T16:13:30.631Z" }, + { url = "https://files.pythonhosted.org/packages/98/48/deaf2326262a8d5ea3295ce9649912ecd3f551ba7ec8e33c665d2ba583f3/msgpack-1.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec0e675d59150a6269ddc9139087c722292664a37d071a849c05c473350f1f2d", size = 396168, upload-time = "2026-06-18T16:13:31.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/2a/b4410f906c2ec0008f1608d3ab5143afc3ad3f4e6da0fed3ea2231d0bef4/msgpack-1.2.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:dd3bfe82d53edfe4b7fc9a7ec9761e23a7a5b1dac22264505af428253c29ed24", size = 371959, upload-time = "2026-06-18T16:13:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/59/86/1edc67270099a528fa2093ea60fe191233cd238e4bd30cfacf7db79fc959/msgpack-1.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5ad5467fc3f68b5468e06c5f788d712e9f8ffc8b0cd1bcb160c105c1ee92dae7", size = 408457, upload-time = "2026-06-18T16:13:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/82/90/8b630fef07d8c5ab457b71ff2c217910c83d333c7a68472c186e87cc504a/msgpack-1.2.1-cp314-cp314-win32.whl", hash = "sha256:98b58bdb89c46190e4609bb36abe17c6d4105ad13f9c5f8f6f64d320f8ced3fb", size = 65942, upload-time = "2026-06-18T16:13:36.056Z" }, + { url = "https://files.pythonhosted.org/packages/16/f1/467b81e98b24dd3885d7b1857728797b4ffc76a7a7483af4fb321a07de3c/msgpack-1.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:74847557e28ce71bd3c438a447ca90e4b507e997ddbdef8a12a7b283b86c156b", size = 72627, upload-time = "2026-06-18T16:13:37.079Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1d/5d8c4c89985feb6acefb82a09e501c60392261856d2408d20bfe4f0360b1/msgpack-1.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:b50b727bd652bdc37d950336c848ef20ec54a4cafc38dce19b1cd86ad625d0f7", size = 66908, upload-time = "2026-06-18T16:13:38.23Z" }, + { url = "https://files.pythonhosted.org/packages/1b/02/ad2afb678b4de94496cd432b581759b756a92c1192d8c767edd6b132efdc/msgpack-1.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8d00f177ca88a77c1cf848d204a38f249751650b601cb6532acc68805d8a8273", size = 86000, upload-time = "2026-06-18T16:13:39.44Z" }, + { url = "https://files.pythonhosted.org/packages/54/74/0b797484013128837f3b1cbb6cea019277c4de4e377dc512b4d9a0f92940/msgpack-1.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5bb9c386f0a329c035ddbab4b72d1028bf9627add8dda41070288563d57ed1b1", size = 86544, upload-time = "2026-06-18T16:13:40.447Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b4/b774d7eb95561739907fec675582f83203cf41c597a418c2589b4bfb8e9d/msgpack-1.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20466cca18c49c7292a8984bc15d65857b171e7264bdcb5f96baf8be238791fc", size = 427661, upload-time = "2026-06-18T16:13:41.574Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f9/3243191dc9937e00756c8bc1b0272fed8f23758e43df2a3b46f533e5090f/msgpack-1.2.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:196300e7e5d6e74d50f1607ab9c06c4a1484c383cd22defd727902591f7e8dde", size = 426375, upload-time = "2026-06-18T16:13:42.936Z" }, + { url = "https://files.pythonhosted.org/packages/23/c7/1693111db9944ba4ad4b67a1e788400d78a0b6af7a6523dc7e4e58f8274b/msgpack-1.2.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575957e79cd51903a4e8495a242442949641e08f1efd5197b43bebd3ea7682b4", size = 380495, upload-time = "2026-06-18T16:13:44.306Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2b/92f86956a0c13e8662f7e2ad630c4eb4db07497b967589bd5245e018b2c1/msgpack-1.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8c2ed1e48cc0f460bf3c7780e7137ff21a4e18433451916f2442c1b21036cd7d", size = 410897, upload-time = "2026-06-18T16:13:45.629Z" }, + { url = "https://files.pythonhosted.org/packages/da/ea/1479f72d200313a76fc2f823a79d1e07ed052ab7b8a0280640aa7b95de42/msgpack-1.2.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5f6277e5f783c36786a145e0247fc189a03f35f84b251646e53592d2bc12b355", size = 378519, upload-time = "2026-06-18T16:13:46.998Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4d/fa006060ffa1011d32bfae826fe766fe73e02982183601633b7121058ab3/msgpack-1.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9389552ecf4784886345ead0647e4edc96bee37cbab05b75540f542f766c48c", size = 419815, upload-time = "2026-06-18T16:13:48.205Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/aab6c946570496b78e67804721f3d5e2d62a93081b9b37df77764ef56347/msgpack-1.2.1-cp314-cp314t-win32.whl", hash = "sha256:c1c79a604a2969a868a78b6ebd27a887e00c624f14f66b3038e0590cb23332d1", size = 70914, upload-time = "2026-06-18T16:13:49.385Z" }, + { url = "https://files.pythonhosted.org/packages/13/0a/e608956488a2af014cfe6e3d665e090b8ee42aa14b07f8f95b8880d66b09/msgpack-1.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f12038a35fabd52e56a3547bab42401af49a45caa6dd00b34c44de235bc93ee2", size = 77999, upload-time = "2026-06-18T16:13:50.467Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8a/27e2e57055176e366a46b85d02d68e7a5bcfbdd8474c9706375d965f24d3/msgpack-1.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:0adcf06ffde0777c0e1a9b771a2b1c4226ba1bbf748c8efcc02fcdeca3299107", size = 71160, upload-time = "2026-06-18T16:13:51.498Z" }, +] + +[[package]] +name = "obstore" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/2f/f83afaab7945509d72245b2b00af0b4834ce78fdd2d9ae9f0ad1a3036a91/obstore-0.11.0.tar.gz", hash = "sha256:a2f55163bcd348b4a60d12e6893eac50eddc742bad8032a1705d49140b992204", size = 130565, upload-time = "2026-06-25T18:29:49.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/ea/3792fd91107068987d96e65b616ce2e7b461b3a04665d69ef91babd3094f/obstore-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:fc61226fecb74125f4bfdb5f8c29e440538814968e304a6df5a41e8becc21f6d", size = 5491962, upload-time = "2026-06-25T18:28:07.095Z" }, + { url = "https://files.pythonhosted.org/packages/ad/a5/60f84c4fdbe8c6f769820544b7dbc7c0595e47bb610fdb32acb87b0240e3/obstore-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2427bf1c18d49c04c5f08763e921ba382b615655dd0ec6544a6f696462c9ecc6", size = 4675452, upload-time = "2026-06-25T18:28:09.5Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d1/558a2500ca0a7808691b13d39c39752b48987105c0f73d1c36b343ab61fb/obstore-0.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:931f58915ee9b71d82951e8c4e438a6823ba7d362f6edde394089bec07c9dd65", size = 5069608, upload-time = "2026-06-25T18:28:11.203Z" }, + { url = "https://files.pythonhosted.org/packages/76/85/1211b6b132a216381c1af7368be6ef7df52294209046de5ec41007c33c8e/obstore-0.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86055c89ebae043dedbbe7b02b07f68f3af7e9d70eedadc5d79805572ffc2543", size = 5298950, upload-time = "2026-06-25T18:28:13.22Z" }, + { url = "https://files.pythonhosted.org/packages/01/f1/78c069aa1372b2d274895cb5b3dec097135a9a981d7ca6335bd7500ae0d5/obstore-0.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51c5e265910bb6502b6246bef9f9f1f9151b8889d8bac549f94ab30eccae67d5", size = 5492459, upload-time = "2026-06-25T18:28:15.222Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f7/103df5872a229fa7619a88c4ee5f181bc02a64dba7424098fece44be9058/obstore-0.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82e59348de18fa332306dd4aceee14af69d64d6626cd3244de3869041dfed461", size = 5361500, upload-time = "2026-06-25T18:28:16.837Z" }, + { url = "https://files.pythonhosted.org/packages/55/40/7b03d2a2ae89b6703e70dafb1b8498b8a615601c9ebe01fbb125f635392d/obstore-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9d7411de9eef9007c971059ca714e50ef1bff24ba95c4ca762c9155b569af4b", size = 5635311, upload-time = "2026-06-25T18:28:18.86Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a9/552ca7e52813dfe26cbddff964d7807c8ac720c6d915066adbc61d8d2638/obstore-0.11.0-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:9be99562b09b32e7b1e51305f9871fa6d96ecf26fa05b7df8f6064be7036e77f", size = 5415966, upload-time = "2026-06-25T18:28:20.641Z" }, + { url = "https://files.pythonhosted.org/packages/21/93/35f974522a5d325e56b2fe5d9d306231f73384216a26d5595382141b8b0e/obstore-0.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d40161c924435ae45c89b3b32f6329ad247b76feec17f3123f92621defa533ed", size = 5622823, upload-time = "2026-06-25T18:28:22.587Z" }, + { url = "https://files.pythonhosted.org/packages/66/b6/99580e8b8a3d26130a299b9f7f5b15ed9fcfaf57a6b69becf59d6e169842/obstore-0.11.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b69b9e6d5fd5f36063fb17eada334fc6e52e3309daed20b04986945dbc0915a2", size = 5297560, upload-time = "2026-06-25T18:28:24.558Z" }, + { url = "https://files.pythonhosted.org/packages/73/e6/e3b70c0472c7848d57c273e172853c0d20ab97ec6491abc034da2ea8c327/obstore-0.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2b1d9568db8af6341c7ca9ecf15bc444c1ac748933b0e0e93fb9f2797a16ea37", size = 5427004, upload-time = "2026-06-25T18:28:26.366Z" }, + { url = "https://files.pythonhosted.org/packages/de/4d/1fa2abe05f9ba3fa010aadb3695ce02e97ab3da6b8e12e46b309ca71f249/obstore-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2a569f81c835c28b247a531849b357249d96753a30b45b4f4c3b442f24d988fc", size = 5865056, upload-time = "2026-06-25T18:28:28.231Z" }, + { url = "https://files.pythonhosted.org/packages/df/ba/ddc091b18baa49afdfc8b270ac8c16bdd69b05bf3e75acd2e20bd5970d9d/obstore-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:545b23c2d13f2a5911a3b1a2c0b9dbf307b430684d171c5a7905c3e514a80662", size = 5323295, upload-time = "2026-06-25T18:28:30.107Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b2/00c213e7e5ca8065f97e37e55294adab836e3f6a88b23e4029069aaecf95/obstore-0.11.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:42f36546c7ac44dbab1173d2330a8a1b1a3f0e37950e553b8c904e3dd0744b25", size = 5491935, upload-time = "2026-06-25T18:28:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/ac/37/6a6b9a5e15a8a37c24d14317a87648097c4888593b588510c03c030d2e90/obstore-0.11.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:687bb9d3962d568b7c439c5d0c6fea19b2749862a8e5c8eebd0c058c4eccde9e", size = 4672619, upload-time = "2026-06-25T18:28:33.852Z" }, + { url = "https://files.pythonhosted.org/packages/28/f9/6745ce8c4f7bfac19dc14a4438b48a2e93a689b92b0cecfc695e41a4e8b1/obstore-0.11.0-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:010b51578c7514a41719d795cdb7a1e6529be509dac3772e477187a59422bb97", size = 5072806, upload-time = "2026-06-25T18:28:36.127Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/991d3b3cdd851c0225e55f3dc45b47fd9e249827d188995011469f805132/obstore-0.11.0-cp311-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfaa8129a3f5d8518a3a75184d4b02348db0f6263177cd1f0951f6568243cc9e", size = 5303777, upload-time = "2026-06-25T18:28:37.89Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e9/90e56015a45b5e56a84fc3188c4e5fb088b288d41992c73a629e10df6760/obstore-0.11.0-cp311-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c790a5cb9ff2970d1f464a6a708d734dce9939e9f668cb6708c5dba5d61589b2", size = 5493871, upload-time = "2026-06-25T18:28:39.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/02/f1744091d59ce71c5523174eb860fbb298275c901e89b9ea6fbf3e654a33/obstore-0.11.0-cp311-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:827113e12fe8088e0281a9d57b90b2b8dbc8a6ffe3b15dadb9baa5feb3d266c1", size = 5361913, upload-time = "2026-06-25T18:28:42.089Z" }, + { url = "https://files.pythonhosted.org/packages/5d/59/3f47822683ee2b6db8685faa25829946d6343a561251ec2704548455d946/obstore-0.11.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2ff6d3ed553298828fb760b4aef6347fbcc7b5c5e3ce3f8381ce805c370021a", size = 5638724, upload-time = "2026-06-25T18:28:43.897Z" }, + { url = "https://files.pythonhosted.org/packages/23/50/1df335fdf9b527b3933f1e94ab6fc720ad314260fab8591cb0b6668ff192/obstore-0.11.0-cp311-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:39d04b324fcf984e7050734ebda77b81764025b0c011750201a0d8954087f7aa", size = 5413508, upload-time = "2026-06-25T18:28:45.624Z" }, + { url = "https://files.pythonhosted.org/packages/de/dc/a259aba149b841ca7c91fea177df9972a60a636b54077beed1a35b254994/obstore-0.11.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:37c0d15d775b1370ef5204ee3919a5ddf7e2592d11815213105f8db031f2ab8d", size = 5619995, upload-time = "2026-06-25T18:28:47.599Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b4/ec25fdb4d6b060bc6eea647fc0e88f75fcc20fe8d16d67fb0dbe999d323b/obstore-0.11.0-cp311-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:7f468caf9b6e0f12ff151e5fe618de5fc9192befa9bd02734b06de4efd2e49f6", size = 5299512, upload-time = "2026-06-25T18:28:49.629Z" }, + { url = "https://files.pythonhosted.org/packages/a8/e5/29be060d06ec13e2af3d1b6cfb77b7c37f8be6c56b77295c945fefad73e4/obstore-0.11.0-cp311-abi3-musllinux_1_2_i686.whl", hash = "sha256:42d8e8fad85be8ee488c1a9a9b7c6a42128abb84e67175da40d3d1165c1846df", size = 5427026, upload-time = "2026-06-25T18:28:51.317Z" }, + { url = "https://files.pythonhosted.org/packages/57/b7/577a965f440e9ea64243518663f9d16be7df8eafc7123818e8e841fa21ce/obstore-0.11.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9c8fd2a544e2e0b926669c47fcfb8d2314e234abc240ea165dae04ee42e1d7ac", size = 5869187, upload-time = "2026-06-25T18:28:53.166Z" }, + { url = "https://files.pythonhosted.org/packages/e2/18/8fdbaee22bfd5b9c44e1fdff8ca0508e2fe60c42bf9fc85f0c9c27b4ecf2/obstore-0.11.0-cp311-abi3-win_amd64.whl", hash = "sha256:6fb3d4678c0f4242d3109362e9b1df5d7b27765f43d5aacb2e81af53a75cb9ef", size = 5329384, upload-time = "2026-06-25T18:28:55.305Z" }, + { url = "https://files.pythonhosted.org/packages/8b/8b/7555e48ec768728fcfc71a051c6b28d6ddaf1bececf492ce5ef995aab5f0/obstore-0.11.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f3132393eff9f3f2b543ecbb3bcc12319a7c433fef06493b4350d6854d505a14", size = 5515763, upload-time = "2026-06-25T18:28:57.527Z" }, + { url = "https://files.pythonhosted.org/packages/23/8f/94d83f3336421cbb5e436ab0ae5695eae72f7c82990d6b1ac090712c8052/obstore-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a3a8da19b47af4c14ecc694209b3c18ac6d89f96be5656ee3a19b77947c14155", size = 4649491, upload-time = "2026-06-25T18:28:59.386Z" }, + { url = "https://files.pythonhosted.org/packages/fe/86/11f4e1f51a8c6cf21a5915c018d2357201ad3c5799d418f0c6529fafaab2/obstore-0.11.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e91298b9b6c3a0408c28eece62bca6c5b6cda2f6350351d84e07b4dc8fb2631f", size = 5060659, upload-time = "2026-06-25T18:29:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/49/e7/fd3036b0923d10e878e2073020f1ef692a618ed1cc3980d3e4a468c93713/obstore-0.11.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3422c532486671dfb5e3e739bf15ec9ca2a8da544a3c23b74ae3857dcab1c6a8", size = 5277058, upload-time = "2026-06-25T18:29:02.96Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a7/b016c3ac6857ac856326dc0a292e3871b8500d03f25f3b90e168b05de357/obstore-0.11.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3de027ce46cf0592c2b41654e2c27dbc52a4a726f6fbc511380acc0da3a9f658", size = 5475852, upload-time = "2026-06-25T18:29:05.032Z" }, + { url = "https://files.pythonhosted.org/packages/85/9e/644ffe8db7757de7f71f94a036ab24222bccb4de290d3ca69f76547e812d/obstore-0.11.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a80a95548678210bc336b866e37139c293565c3b163eb3fef2433d5d6640a33", size = 5363082, upload-time = "2026-06-25T18:29:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/ae/0b/26af6b5fa6ba96af84086f44f78b4e5b0af1729c31402d7b28c68989d174/obstore-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acaa261dc15efb95bbeca06f8fe9b47ee23d7302a6aa1fa3a9654baab8b23d7c", size = 5629116, upload-time = "2026-06-25T18:29:08.771Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ce/f30d502991c6719b2fbd7b8385ef3e39da07bfca099108bcc5eeed8b9c20/obstore-0.11.0-cp314-cp314t-manylinux_2_24_aarch64.whl", hash = "sha256:a3300cabbc3129670987b3723629c791d83c117ef1b6a0c670c2043648e000a1", size = 5404534, upload-time = "2026-06-25T18:29:11.294Z" }, + { url = "https://files.pythonhosted.org/packages/52/20/d5bf5f816e868717ba647ed9a2109e800deb402d0265d410456b3fcb4376/obstore-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f4e6a9480843645cd4ee122c41d5c5a46a56f1e9cdda85638826f2e0e439fe5c", size = 5613159, upload-time = "2026-06-25T18:29:13.414Z" }, + { url = "https://files.pythonhosted.org/packages/db/b4/6d4c1c211e3b06cc8554189e0d4406e8fa1f98ed55f9213b8e398a11599f/obstore-0.11.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:9fb2b1814c4314b8903f4e2ebbe8c3365fea6543669615ee9a0288b0d3a2edeb", size = 5286279, upload-time = "2026-06-25T18:29:15.424Z" }, + { url = "https://files.pythonhosted.org/packages/02/5e/d7b5589424a56171b16ab94cf92eb493490c300aaa044913bbdd94cace68/obstore-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63fb9b072815eafe4705f617f567d896b1c858adcb390795fa1e269367791031", size = 5401780, upload-time = "2026-06-25T18:29:17.514Z" }, + { url = "https://files.pythonhosted.org/packages/c4/18/841baea8936e51a18b0e5d4c51f09c0a7798cb73b027e9794be2362a0f0b/obstore-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:086fafba314ff98cfab1c4bf7814699e862513e8889720cf6f7462296cb32787", size = 5853618, upload-time = "2026-06-25T18:29:19.354Z" }, + { url = "https://files.pythonhosted.org/packages/83/9a/d6127f5422b78e0222b0a9eadcfd7a5aa8d873a9498da7d4a77d4ac8ce2e/obstore-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:676d1154f6f08721110f9b7d14ee3a3c0293abaf9da135bb90f54e276dca1cac", size = 5314113, upload-time = "2026-06-25T18:29:21.209Z" }, + { url = "https://files.pythonhosted.org/packages/61/17/6ddc3e035a0adc3397bc2b9ea4ebe52db6711ad87ddf0180b7675fa25d6a/obstore-0.11.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c30aec09acff505e27b7392eb5e4b7bb7073d3f21e44ea43a64913369d83cba0", size = 5500652, upload-time = "2026-06-25T18:29:23.423Z" }, + { url = "https://files.pythonhosted.org/packages/7c/66/0e8ebfebf7151b401dee19373b3a699b179c3687bea5d684adbef2c7c67d/obstore-0.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:32ec11db91b85e4482baf2c8c0f43b469e8788765cd844839c84468516446181", size = 4681675, upload-time = "2026-06-25T18:29:25.766Z" }, + { url = "https://files.pythonhosted.org/packages/fa/84/717505cdf8e453ef16be74e6ca4204629722227c1cbecf3719a17513dd9a/obstore-0.11.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a7f574cf222156f95846fda755365085e8c825be48359f32fa57935fa79f172", size = 5078712, upload-time = "2026-06-25T18:29:27.502Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e4/a3a87a9ef6973cc59f8cd3543b74cde3bac81ff18bfdab80a460c95d5df0/obstore-0.11.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:193d8af924c24ab60c342d64c55896f8cae026851182ade6f1b650789d17b84a", size = 5309116, upload-time = "2026-06-25T18:29:29.912Z" }, + { url = "https://files.pythonhosted.org/packages/98/40/af6699c140cd4f5fb638ccad2053b096ebaa8b5fcd2a2c5176113c1bc847/obstore-0.11.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed68e3653f12a995bca4372cdccf8663bd3df2ce9750132d11c4c04489e2dc61", size = 5503037, upload-time = "2026-06-25T18:29:32.396Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/e83123e8e2d2075dd686ed1c13e462c19b9c57ec0a67c316bdde862c6def/obstore-0.11.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee8d5aa13158e39d20e76730fdc81a50fd80f6d415a1dc327cf317e5c4e2e878", size = 5368241, upload-time = "2026-06-25T18:29:34.366Z" }, + { url = "https://files.pythonhosted.org/packages/8a/94/d1cba6347ed6e540765e6db2c2a262f8aba50ae40a0d47749465c42e94a0/obstore-0.11.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca0a6bef07fc26990828528090c6b41860ca949d3cf1b81b24854674173b47ba", size = 5644399, upload-time = "2026-06-25T18:29:36.482Z" }, + { url = "https://files.pythonhosted.org/packages/d0/24/a9033feffb423d5f6a7bdc85041e9b9e1f67b2835fd484427d005ed17187/obstore-0.11.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl", hash = "sha256:c9c6ccc06801afb0fc0e8f0e8c957f643c2c4f7e349eb36a736167e280709c6c", size = 5423124, upload-time = "2026-06-25T18:29:38.513Z" }, + { url = "https://files.pythonhosted.org/packages/69/e8/2ee75c66bc703670bbf1aa51c320a4d282efa85adf8e9fa3f7467c02513e/obstore-0.11.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9555f36a0724d34d747b38cda0cb55b3f650a22e8671614037c52190bf4da6cc", size = 5630049, upload-time = "2026-06-25T18:29:40.647Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/76b3099664d997747e5e7b11b322926d486d1471266941fc0103d912b666/obstore-0.11.0-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:f0c1527daf1c75d3f70ee3f6bef2767d8b07005cd7a5b43dc8c96a1323f3ac54", size = 5308482, upload-time = "2026-06-25T18:29:42.643Z" }, + { url = "https://files.pythonhosted.org/packages/34/96/03594ac63d7b1a0e773b0c604c38003ee828e6d5348488eaeb2c57bdc29e/obstore-0.11.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ed5910bde525d7d936d36dc78c7d4c07b57b6c9b55e402357b107103595d56ff", size = 5433377, upload-time = "2026-06-25T18:29:45.323Z" }, + { url = "https://files.pythonhosted.org/packages/2c/53/07226c948760264fb7ba253825ecbe5941abfac9875e6d1b2679cc82e3ef/obstore-0.11.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:65c0208ead516c1a79afced16f2cda9b5542886123e53ea0a8a7d5d56b86fba5", size = 5868708, upload-time = "2026-06-25T18:29:47.668Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.44.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ee/8b/aa9e2d8b8dfa7c946f7dec5d1f8f6ba8eca062f43509a06bdb5ce93d26c0/opentelemetry_api-1.44.0.tar.gz", hash = "sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a", size = 72406, upload-time = "2026-07-16T15:25:32.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/6f/a04e900f465ff3221ccc395522503e2d10e79fa21f2723c8e177aae1e0d1/opentelemetry_api-1.44.0-py3-none-any.whl", hash = "sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef", size = 60018, upload-time = "2026-07-16T15:25:11.657Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.44.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/77/a6592cbc7c8d9bcc9d6757a9df45e04a7c585e3e6e7a13456da522b21109/opentelemetry_sdk-1.44.0.tar.gz", hash = "sha256:cebe7f65dc12f26ead75c6064de12fd2a9052e5060c0272d402cfa203aae123b", size = 208624, upload-time = "2026-07-16T15:25:46.078Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/23/ff077e61886ee020a17ce9c8b6fa11c601c8d8345b09ea24f605445df62a/opentelemetry_sdk-1.44.0-py3-none-any.whl", hash = "sha256:df081c4c6bcfdb1211e3e86140376792643128a25f8d72d1d27675936e7e96ad", size = 137221, upload-time = "2026-07-16T15:25:29.534Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.65b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/73/0cbdebcb4cf545fdd328da14f5137e37d0770c3f26185e478b0d15d94f50/opentelemetry_semantic_conventions-0.65b0.tar.gz", hash = "sha256:f9b2b81e9d5b64f11bc952075e7e9c7fb0aab075c7fd1c46d597f1b919852d60", size = 148774, upload-time = "2026-07-16T15:25:46.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/0e/49df70d9b81fb5cbae4bbf2a49d865b09bcbcbc4eb53f5851b1027738d78/opentelemetry_semantic_conventions-0.65b0-py3-none-any.whl", hash = "sha256:1cacde7b0ad306f84c5ef08c3dbe1bbaf20165bba6f8bff43b670e555a086bcb", size = 204645, upload-time = "2026-07-16T15:25:30.688Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[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 = "protobuf" +version = "7.36.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/e7/0553e21d25ca4d9f573135775348a372c3ec34a93a71d5f297c3bac38341/protobuf-7.36.0.tar.gz", hash = "sha256:e8e09cb0d794c6687926fa558a8a6e72aa10edb997d5ca61da0765f12a3e00ea", size = 510034, upload-time = "2026-08-20T16:34:01.071Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/ae/58e3ca96cb2e118cc546b677359b3c6659f79a140935c08dec94c7998585/protobuf-7.36.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:9103532dffd80c6fab7e50c65a31007680a06eb57537d437bb1b35812c138a37", size = 453256, upload-time = "2026-08-20T16:33:53.945Z" }, + { url = "https://files.pythonhosted.org/packages/f0/15/5162230af4912697f0fe406f6800f80760945babcff0e2c2fe6c84ef2d5d/protobuf-7.36.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:bf94a5917c71058262de683669bc0a797a7669d3de71f0b36d058e3194f47b44", size = 341436, upload-time = "2026-08-20T16:33:55.134Z" }, + { url = "https://files.pythonhosted.org/packages/d7/09/1670b2bfc9a45e807e520c3e9be36524db9ccc7dc05ea17af7681cabdc61/protobuf-7.36.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:3297e60abdff301e5f74393d87f6cc59dacab5f024a89548a6e8de1d26576b16", size = 354440, upload-time = "2026-08-20T16:33:56.077Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f8/bd5804695ba400e423c33fd4d9f58c28d86633d5ba1945c36ff3967d98cb/protobuf-7.36.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:70f5ec8eb0da81a44360c0dc0beac99a0d78071d21956a7076bae8bd2051841b", size = 340439, upload-time = "2026-08-20T16:33:56.992Z" }, + { url = "https://files.pythonhosted.org/packages/ef/9f/acd02338235a3e7d03168c4303478347b7624fc8189ff4e7f0d2654bbe86/protobuf-7.36.0-cp310-abi3-win32.whl", hash = "sha256:7326fd717bdc419162a735938d89d4032332bcc3408804012b24ff3a37086071", size = 440216, upload-time = "2026-08-20T16:33:57.99Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4e/12cb93270967a2affff5b3f720694700d4d87712a67afd05c8cb3f6fa52c/protobuf-7.36.0-cp310-abi3-win_amd64.whl", hash = "sha256:1781cc1de61249b750848029bca452c0a8b7e990080316b9bbc2518b2117b488", size = 453731, upload-time = "2026-08-20T16:33:58.951Z" }, + { url = "https://files.pythonhosted.org/packages/01/c3/629999e78d46c1115c11886d51c6bd68c17ce4a944f1ea3e153a91316a33/protobuf-7.36.0-py3-none-any.whl", hash = "sha256:53374d53fc29a67f7dbbf0ade47d7526a0f0137bf0f9c90e48d8a60790ef748c", size = 177024, upload-time = "2026-08-20T16:34:00.053Z" }, +] + +[[package]] +name = "protobuf-py" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf-py-ext", marker = "(platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/fa/cd8192cc89c7373a8864f310b423faa0ca23679f68e70b16abc7512e2570/protobuf_py-0.3.0.tar.gz", hash = "sha256:3fd187380a85b9850ec548b324eb1a6f5e3a70d62d483ef73c4ed62e0791c26c", size = 157327, upload-time = "2026-08-07T01:24:24.066Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/1a/764caf932a88d63cd68f2d53d3cb34a62c61c252f22536ed7fb1050c378c/protobuf_py-0.3.0-py3-none-any.whl", hash = "sha256:821d2b48f83c98e0c6b8eddc1f7b3ab89f6ea5c5f797952c716a5bc300290923", size = 205193, upload-time = "2026-08-07T01:23:40.072Z" }, +] + +[[package]] +name = "protobuf-py-ext" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/b8/ea997e3f3c29ec39f0dbe9cd64482e08d1a0f0947ccb81fefb1bc53e655b/protobuf_py_ext-0.3.0.tar.gz", hash = "sha256:d6a34587eceb5ef6777fb3dedc1c61f1192da71d5f79049a8028ad06313d2d84", size = 57167, upload-time = "2026-08-07T01:24:25.117Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/27/20658c04dd4c403d82b5e5f3772f15ee9a1e9ce2898c2f2b35597e613427/protobuf_py_ext-0.3.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:32757358a6e3b2f56648eb7268dd34db06f97bb95c501d033fcb6435a9c16bb6", size = 469520, upload-time = "2026-08-07T01:23:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/00/3e/c9bb496284355b19b13e699a4a47af9a82fe75a7dbaf17e80a014827cdc3/protobuf_py_ext-0.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddadde8fc52dbd5baf95fd9399696da4da198a8d8f5d26e0d3b82ce78003690b", size = 476076, upload-time = "2026-08-07T01:23:43.663Z" }, + { url = "https://files.pythonhosted.org/packages/78/12/698b371bf31c9ae9932739fabb8455f59dcbbe37efade5fed7f0bbf06d6a/protobuf_py_ext-0.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f3c1a6fccc04baf459ac9b6c6d081ed865e55df3d28da912c7ac853fff22a5a", size = 499103, upload-time = "2026-08-07T01:23:45.131Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f0/710711ea30665aa90bc9f06eb03b9c08c214b35983375e9f8500c650efd2/protobuf_py_ext-0.3.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f02991fce2386d17995bda2ad95131c23eda6391812822e5dc884798a6cfede", size = 653224, upload-time = "2026-08-07T01:23:46.688Z" }, + { url = "https://files.pythonhosted.org/packages/48/88/38c152ec0b1e3a44f0f9c3391022c1ee503fb09d6bb55343354aa6252708/protobuf_py_ext-0.3.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:177214198ebc2b4553375e84cef09d68be8e2b72a914420b5207436a3069a99b", size = 711813, upload-time = "2026-08-07T01:23:48.083Z" }, + { url = "https://files.pythonhosted.org/packages/17/7f/d847aed3628f4f29c33c697856a6f1d9de7cf771bdc933b3f50cbe72ad22/protobuf_py_ext-0.3.0-cp310-abi3-win_amd64.whl", hash = "sha256:b9dec4035f401ebe146e10779ce688cd49baf19e4b452552b8df37211df1377a", size = 435724, upload-time = "2026-08-07T01:23:49.717Z" }, + { url = "https://files.pythonhosted.org/packages/54/4c/b7c9f00d117f74e6639e48a85ef59c2caec9e90cc97c48319b910a87cd67/protobuf_py_ext-0.3.0-cp310-abi3-win_arm64.whl", hash = "sha256:7c9577afe77f0b92ac43a81422c45c46a4aa3bc46adcdbb763188adee4aa0393", size = 417445, upload-time = "2026-08-07T01:23:51.063Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b6/e9a7c4819e71e43d71aa2315c82b904781eae550a398c226763552e274d6/protobuf_py_ext-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba996bff51f8b90442968abfeb0a55d0bc6013efec376b2fe36f27fb75cb43b7", size = 467227, upload-time = "2026-08-07T01:23:52.482Z" }, + { url = "https://files.pythonhosted.org/packages/c5/49/2a5ad5abde91262449312336843ca4190aebc154e48e956954334952c767/protobuf_py_ext-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5a30f5c2097f1ba47a2e378749a0922b6b2f2893e006219eec9bb23fba4eb9b", size = 472711, upload-time = "2026-08-07T01:23:54.417Z" }, + { url = "https://files.pythonhosted.org/packages/82/00/e3a998a7307ae92de087e26a8d3fddd78f024517b6a423b1944e4cbce077/protobuf_py_ext-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb0d0850fd505e79becf44c57e96181e3d667e48e726d23be52b7379805a906", size = 493376, upload-time = "2026-08-07T01:23:55.964Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f5/d6363c578dec80a49ae949feb0d0b60c2b2532a1aabb62e56f6f8afa497c/protobuf_py_ext-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b4ffdd5d90fe64ae596c6e3a52d2eaa478e9076bc3a0b903310326fccb8de99", size = 650000, upload-time = "2026-08-07T01:23:57.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/19/4317642a8c57c54271d63d4ee697c6b945dca9621a4455727f5d379ac28c/protobuf_py_ext-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:264d3eb5fcf9cd94e75d26e4c6aa139a49c43bf9d5b8c43d1683b845ac8e3eb3", size = 706070, upload-time = "2026-08-07T01:23:58.953Z" }, + { url = "https://files.pythonhosted.org/packages/58/71/ecfb4d93d5485062b6dc45ff71f746ccd102089ca480127fbba3c1ed1156/protobuf_py_ext-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a01b6df42a9f813da653f8d07937086dd1d8b79ecbb557bde812fb19223924d", size = 467279, upload-time = "2026-08-07T01:24:00.565Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ff/b7f3cd77739ef4644618e334b37a44da15b722a220facb947e1d02e09680/protobuf_py_ext-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b625feca85f01237b9f2a3c1a729592423f5c8ab4f30b183059eb527d695c148", size = 472727, upload-time = "2026-08-07T01:24:02.016Z" }, + { url = "https://files.pythonhosted.org/packages/21/11/551d0f6d5bc57ed0cf857f76969b115c9c41866c004e091b9c6b3ec0124b/protobuf_py_ext-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7acb648ec9e547d65742260e56104244b4e366efd5dd8e965a5d3d8c397c2bd2", size = 493745, upload-time = "2026-08-07T01:24:03.64Z" }, + { url = "https://files.pythonhosted.org/packages/fe/8d/fb42e248ae91e6bf09127597ad741df88f9b869ca5816eea0537e53946f0/protobuf_py_ext-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1f2b9935518b1f4a4359b08e123a272ca7dc23b75db979a80a8b33548f26f78", size = 650196, upload-time = "2026-08-07T01:24:05.025Z" }, + { url = "https://files.pythonhosted.org/packages/00/86/9ef8097465106802733f933b6e4bdf6ca7360419fca00bf9e4924ac1c894/protobuf_py_ext-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3f79b520ec328228307102c8ade14f5ee9129184184894a87a37567fe9f0157c", size = 706523, upload-time = "2026-08-07T01:24:06.431Z" }, + { url = "https://files.pythonhosted.org/packages/51/9e/1338bf38012532be011c08509cdc11383db35e4fee0c3b7977fde6b0c572/protobuf_py_ext-0.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:875c6fdcaef63081373785d62ae34cf06b52ff9fffa48edb2b5df51a2693a989", size = 465843, upload-time = "2026-08-07T01:24:07.912Z" }, + { url = "https://files.pythonhosted.org/packages/38/20/c64703ed9cf852cc7db2af05a427878f2eed1b1fbb2cdcbbb4a2c93bbb3a/protobuf_py_ext-0.3.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187f4ce00a965d8d90af9584436908234ccddb57fa88bffe7d2f0b8a331a9305", size = 471390, upload-time = "2026-08-07T01:24:09.295Z" }, + { url = "https://files.pythonhosted.org/packages/6a/41/8d3f86f665072fd00eddaf229e9eb257cff947d05c107c7e9d34474dadb2/protobuf_py_ext-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b78b7c1cc18fc20e668b59c7ad14d0e183c3d8c6404dc6ec8d3c341aa13d87a4", size = 492409, upload-time = "2026-08-07T01:24:10.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b5/2edbb76e9ec41296ec085ecc888ec50d09eda297744ac3256b046362226a/protobuf_py_ext-0.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c3cc0dba2a4d6c17db15904e82e89b308e6e87cf206fc0b86c00dc72237d77e1", size = 649031, upload-time = "2026-08-07T01:24:12.408Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/a2b7f35e6f25756e6def7938d6a7d976e7cc7e4b5fe3b54c6ed906d9e893/protobuf_py_ext-0.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0ddd90ad7bb65e9f74c4a85d3c27677ada1bfb830ae6b09663adb5a5541d0800", size = 705347, upload-time = "2026-08-07T01:24:14.174Z" }, + { url = "https://files.pythonhosted.org/packages/58/d6/34472bc211133dceff10e9ed1fc319617ff98aa178efcc432080e07664df/protobuf_py_ext-0.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c8fde4998c0c1785871c26851c08d2d6018ef9b56af04b82e56259fc487c146f", size = 454451, upload-time = "2026-08-07T01:24:15.494Z" }, + { url = "https://files.pythonhosted.org/packages/52/70/d2063415e9a7bf9f67307b9d4265ebd47cacfb615ee38047c976a25af1d9/protobuf_py_ext-0.3.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16b55408ab00b9faf9d409c16ce08664769e8448ea32650a43a5e24c60a69481", size = 462784, upload-time = "2026-08-07T01:24:16.99Z" }, + { url = "https://files.pythonhosted.org/packages/bd/68/f7ba7e3200d4b6b5f704c1e4f7df50968a56e07cd583b05c1d2dd745dda8/protobuf_py_ext-0.3.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cae6fea1702a74cf585827bba0b564cecd7174d7edb7e130a10b2d47345f9d4", size = 483419, upload-time = "2026-08-07T01:24:18.439Z" }, + { url = "https://files.pythonhosted.org/packages/16/6e/f1eab75a71f771447b805f46c589f045b1f7b4b04e3190a4f44147511d57/protobuf_py_ext-0.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84336ac13eaa4bcd3fd8562dbfd358d014bba62797dfc9357b7d876700d4a56e", size = 640747, upload-time = "2026-08-07T01:24:19.901Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8e/b2ad1ba6c60a4eca8aab7dfe22765723595dad6432e2f0c067bb8aea4a57/protobuf_py_ext-0.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:525ce39c8f2a12d5670804a1e1cae15e7e7debd4a872a4acd177703fa6b305fd", size = 696495, upload-time = "2026-08-07T01:24:21.451Z" }, +] + +[[package]] +name = "protoc-gen-openapiv2" +version = "0.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/d2/84fecd8df61640226c726c12ad7ddd2a7666a7cd7f898b9a5b72e3a66d44/protoc-gen-openapiv2-0.0.1.tar.gz", hash = "sha256:6f79188d842c13177c9c0558845442c340b43011bf67dfef1dfc3bc067506409", size = 7323, upload-time = "2022-12-02T01:40:57.306Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/ac/bd8961859d8f3f81530465d2ce9b165627e961c00348939009bac2700cc6/protoc_gen_openapiv2-0.0.1-py3-none-any.whl", hash = "sha256:18090c8be3877c438e7da0f7eb7cace45a9a210306bca4707708dbad367857be", size = 7883, upload-time = "2022-12-02T01:40:55.244Z" }, +] + +[[package]] +name = "protovalidate" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/73/f9ec1cbeb4caf28e695e966ba5b13397c598f523170d305ec05d03309b03/protovalidate-2.0.0.tar.gz", hash = "sha256:f2fce76be2afc91f1c2f57b0d1bba26a1681cf687ba387a3a4bea6a69e13a805", size = 18878241, upload-time = "2026-08-19T01:09:50.795Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/eb/3e599ef96878cf389927ad2d041c7c6d7cd76ea8d91a77819523c78d2afc/protovalidate-2.0.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:fd00fbe5297165f55aa208159e28eac27db5821bb255a65d53f659dcb4eba0cd", size = 2729581, upload-time = "2026-08-19T01:09:23.417Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ee/88f2f3a1bb548809e7c4cb000e0beee86bf3702f47b85009c23c4ecbfd75/protovalidate-2.0.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:156efdcc8a7db502f92180f5dff6dea200f8297aa2280abff2b6ca695a62a694", size = 2542469, upload-time = "2026-08-19T01:09:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/64/23/5967208bb04833d566fe932f87e00bece53a30bfc5a54009a56dbf64932e/protovalidate-2.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6109644d4d7be9e49e77198deac274881a281e168a0d2edefa38063e4247280e", size = 3323752, upload-time = "2026-08-19T01:09:26.892Z" }, + { url = "https://files.pythonhosted.org/packages/66/b5/fe5c545bb2daee3d098360604e5c34e6419c9b1e6341890a86e89da4c055/protovalidate-2.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8c9e237b9f034269f134d4ee0a872689b38e1c8113668a351f9f12bcfcf62e", size = 3588717, upload-time = "2026-08-19T01:09:28.308Z" }, + { url = "https://files.pythonhosted.org/packages/8b/dd/677c2adb9a6ca5e7122a4558c469738c93eb88d720fd291b03a6a6bdb375/protovalidate-2.0.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:be86a8253fd64a250d8e15598fcebdf3b1fd6384f6ef1f1d412b4b5cb7d47686", size = 9569483, upload-time = "2026-08-19T01:09:29.933Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d6/7650ad5e8f23a6e0b8e4e36ea8e33c2d3ebf5a8746d49b7c3cee1d156e2a/protovalidate-2.0.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c88442347f2b01fc7a5d67280a99730135310400b039fe63a5ec1581c26ea8b8", size = 10139193, upload-time = "2026-08-19T01:09:32.01Z" }, + { url = "https://files.pythonhosted.org/packages/76/b0/35bde6aa90d2205ae3913e023751067acaf724a3a1e9325cc4d5abcd44ce/protovalidate-2.0.0-cp310-abi3-win_amd64.whl", hash = "sha256:4d4c48b64918f9da1b11cebc66cbe248a942dc42ccbfb27c32834faf9ef68348", size = 2733195, upload-time = "2026-08-19T01:09:33.872Z" }, + { url = "https://files.pythonhosted.org/packages/53/66/1b88effa91f586a4ca13614ca753d1d0ac89728970f6939e85f8adc47aa7/protovalidate-2.0.0-cp310-abi3-win_arm64.whl", hash = "sha256:7dbd2115b0be40139badff0da80fdc4b9d2b08944ccf072bbdefaceeaabac1de", size = 2604461, upload-time = "2026-08-19T01:09:35.44Z" }, + { url = "https://files.pythonhosted.org/packages/df/13/604ace01a46ee76615b6c00bffb2e52bd9f4239d8df1f35a80114fd72b34/protovalidate-2.0.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:c543314a58605419c6ebcfed9559bfbf73ec34e0f8c1e42f25a93a5d32fac29d", size = 2728591, upload-time = "2026-08-19T01:09:36.846Z" }, + { url = "https://files.pythonhosted.org/packages/dc/24/1d69b31c89868f4047a0b9bc367077d55a89b9fe42831309d3f24291fb89/protovalidate-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9d632d66535c29d86eaa6d7148b66d8e9ce76cf6475f1c820b39bc197e67d356", size = 2533581, upload-time = "2026-08-19T01:09:38.254Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1e/8260ab04a9bcb50393dc9b34cf977f7b85d50daf7a6b78118f37ad428547/protovalidate-2.0.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb63d5c09687248ba583b917b368ce6e214890a1e86831dc80ed54e72967718b", size = 3319591, upload-time = "2026-08-19T01:09:39.855Z" }, + { url = "https://files.pythonhosted.org/packages/be/2d/3ffc43b6bdec33232041311b9aa58a7e67b097ca03b807ac099f46b9e750/protovalidate-2.0.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ce2601b2734cc0a5e8fce1d8c39dfcabec8033a271ddb140e9eb3488765dae2", size = 3583669, upload-time = "2026-08-19T01:09:41.4Z" }, + { url = "https://files.pythonhosted.org/packages/40/2b/233c0a97ff1941592e271bb38b95df5fc538a5e3bae48745f45ec040e404/protovalidate-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c3e105e5304fe74295a4f52d0aace8382f7b1934dcbbb396fa21f475e7d1a5d3", size = 9565307, upload-time = "2026-08-19T01:09:42.956Z" }, + { url = "https://files.pythonhosted.org/packages/42/b2/02960bc493fc67b9d215c47437645a2581e220cafc817be535fff5f939c3/protovalidate-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffc55c8988f00bfd2a01cc951fb31a217ccd07e27b5b0d23d811be8754e562d7", size = 10134757, upload-time = "2026-08-19T01:09:45.315Z" }, + { url = "https://files.pythonhosted.org/packages/f3/33/a922f91110f8ef7eb58da6bf2c883c6592a31af641a17039f51e626ce02b/protovalidate-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef05e596a5a7b333b33d45c02c81a95db6d6c291c520716c36031a691925647", size = 2726317, upload-time = "2026-08-19T01:09:47.329Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d1/2bfed5e8228d1c90e651a879fad7136e06e82dee2ea0d9063b82f87c2104/protovalidate-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1e797abc70fb89513d2556a85c89d066c8204f2f5b84944f0a485d5e0c87567b", size = 2597755, upload-time = "2026-08-19T01:09:48.909Z" }, +] + +[[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.14.0b1" +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/eb/08/d6380e1986e14e0d51f8ee498bd97c437992d723d1ea23ad3be92e8bd341/pydantic-2.14.0b1.tar.gz", hash = "sha256:d974b3fe7e6ad2a3a5718842d4fc3f5f78142d431a8f73d2d08bc498acf39c8a", size = 865425, upload-time = "2026-08-06T14:29:30.306Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/77/0deeee9facbe51c1c2dd430ebe785255112d8262663b46a7090ad691a5ba/pydantic-2.14.0b1-py3-none-any.whl", hash = "sha256:c7803eac0d891142724e06f9a53264feb1b4e3dfdf89890cbe7eb819795f6b07", size = 477719, upload-time = "2026-08-06T14:29:28.507Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/1b/34440c0294cbc90e57873cbc28465a7e6d6216984cf3ce195cb8ca6791ee/pydantic_core-2.48.0.tar.gz", hash = "sha256:8714f70dafdffea0a5596cc88eddbdc71f5856563947970dcbd0f1ced61ed05f", size = 479692, upload-time = "2026-08-06T14:27:05.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/29/28c42e613ceb1e8e7eafba6aef41156c3da6d3b41b0d132f638b164630e1/pydantic_core-2.48.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ef32e1fc6daeb6b9f0112e121911096d28563dbac2d9bb6f4294cadbdbf5004d", size = 2062969, upload-time = "2026-08-06T14:21:51.964Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/3d86a4f8fd7ae4fccc730f32b98640415a30bf0ecc931ae46bc4c80f6cef/pydantic_core-2.48.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9129dc3ec5b8936078d512707a70dc591f536991d013bc952699714a41f73b73", size = 1967527, upload-time = "2026-08-06T14:21:53.99Z" }, + { url = "https://files.pythonhosted.org/packages/54/ac/0a5889309f0ed7a0e9f8ae81d432d3a826b590ed35f7997825e05736ae5e/pydantic_core-2.48.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e2253856265218d43afb673823f1c0d3863b9bbaa26ca0c0861402385f7e49", size = 1968939, upload-time = "2026-08-06T14:21:55.625Z" }, + { url = "https://files.pythonhosted.org/packages/41/39/ed38322a274b4eb54dffcc502c7220d89195b8460119ff0cf2fe25b7168e/pydantic_core-2.48.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:227a0439fc99197defc197dc0bac8c72474c681a808a6870cd62bfd4b0a4ab56", size = 2079159, upload-time = "2026-08-06T14:21:57.108Z" }, + { url = "https://files.pythonhosted.org/packages/43/18/eb1cf9182c0108535f9b8843773bcefd261bbe38afa566e60df601c67d2a/pydantic_core-2.48.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0775550fa7bf15ed97dba9ef37be1cbbb6f5fd79a53a51c78d4e8823e456f575", size = 2266288, upload-time = "2026-08-06T14:21:58.493Z" }, + { url = "https://files.pythonhosted.org/packages/70/a7/453dc3c96ee544a62e3237212b46d8b1f6d6947c2d738944fa58cf23f17a/pydantic_core-2.48.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78c66e6a8a9981dfe93c0732ebf51af9f3296255c72a5d9ec29159df2176abff", size = 2289188, upload-time = "2026-08-06T14:21:59.983Z" }, + { url = "https://files.pythonhosted.org/packages/04/3c/b7600b24168fc62fc4fa2c45842d8985b6e0afb69f690b7e37d09493036a/pydantic_core-2.48.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e161be86326c9a8e263bf6ba9f26d1d48b68aa90ce07bbcd6b2c8c7565200a88", size = 2082249, upload-time = "2026-08-06T14:22:01.71Z" }, + { url = "https://files.pythonhosted.org/packages/1c/80/408fe40f2e6ec0a2f1a38c66434f5452d47a0dfab6f131d810d325f0944e/pydantic_core-2.48.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:85689e2ed2edc5bd2e207135400e1d9cb9448dba11f31b6df1529b65059d8624", size = 2152562, upload-time = "2026-08-06T14:22:03.246Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f1/0160b219e6d91248676c7649bbbbe91c08acaa29daeba89dc9fc7b49e43c/pydantic_core-2.48.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:156155db186b956a4e957d1a31d5d0d4f621a27867516e67c198111dcbb694eb", size = 2210308, upload-time = "2026-08-06T14:22:05.028Z" }, + { url = "https://files.pythonhosted.org/packages/83/94/e8671a36d9efcd0a18fd01ec2098d9a9f414ae2e28ee32859286ecc51d88/pydantic_core-2.48.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:69a2d08a38b454e6ee16824e15dfac4035de929c41378df021d8a7a67ee99e16", size = 2196059, upload-time = "2026-08-06T14:22:06.586Z" }, + { url = "https://files.pythonhosted.org/packages/19/57/f94ce5e0c40aec020cf2cb672704c0cade9312368f6cdbca445be01caf16/pydantic_core-2.48.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1dac2c7bcb41e8f8b2b7e9937348f7ed94e63ba9d544b2c724b5e4adf800905b", size = 2353555, upload-time = "2026-08-06T14:22:08.091Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c2/bf39ad5bb631daef40a940816be4d7bb0d964f654745ce1bf6e3108560c8/pydantic_core-2.48.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c77b4335446405f056287f34d3145b0f79c49aa2e4b93023c8708f1c3f50688", size = 2376174, upload-time = "2026-08-06T14:22:09.891Z" }, + { url = "https://files.pythonhosted.org/packages/8d/45/d4986939709fc978b0baff912741ba66262b8aa9fb5d46f1452e588b697d/pydantic_core-2.48.0-cp310-cp310-win32.whl", hash = "sha256:b9027262f14fb58e36f0fd617c285bf10b2b3ea31043771bb4f1b9a27fd795a0", size = 1992777, upload-time = "2026-08-06T14:22:11.342Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4c/a02c7c06c80e8f268109fbddca99bc076686dcbdc337a668086506947d03/pydantic_core-2.48.0-cp310-cp310-win_amd64.whl", hash = "sha256:f21799be4afd02eb9fd2309dc076a5717fffee48b4d5536383f2281db0607de7", size = 2084089, upload-time = "2026-08-06T14:22:12.801Z" }, + { url = "https://files.pythonhosted.org/packages/92/bb/a54b3196a20aac8884a183a16ee2886e3761be99935081de415e6ddcd4b7/pydantic_core-2.48.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:1634b4b2687380472d314ee0dce7a98c056da3f6071fed2df52e4f70936f1f4e", size = 2059543, upload-time = "2026-08-06T14:22:14.277Z" }, + { url = "https://files.pythonhosted.org/packages/42/11/243cf4d760624bc48b2880dccdb266b53862c0ff9a66ac2fd174f4c909f3/pydantic_core-2.48.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbad852aa108d223e45d91bf032d5ae303295227177ebcc0ef9c343d88db6aa5", size = 1963087, upload-time = "2026-08-06T14:22:15.729Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/8b4a9ca27d22174b6588946936db98311af587e9c121ed401cd1f1835f41/pydantic_core-2.48.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:024f06afbc680c6eb6edb6db02754e7bfa453e7fd943da05a77cceaf24a408ad", size = 1966811, upload-time = "2026-08-06T14:22:17.268Z" }, + { url = "https://files.pythonhosted.org/packages/8d/93/71369bf1ab25af7f01f52e449e33c81d68571c412d84e3720cf9b50f5858/pydantic_core-2.48.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3645e574266173554d243a414f9200df4f82350fed68e66402f2b1fd5fd5f09a", size = 2075678, upload-time = "2026-08-06T14:22:18.789Z" }, + { url = "https://files.pythonhosted.org/packages/ef/40/4b58304e5489b20b1483b6826ecbf14ce404188a9d6d19105c45f65f62d5/pydantic_core-2.48.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bea268173cc91fa4bf5b4ac80a9b9425a8aa8634c8d2cd94208c4dd2cf67af6e", size = 2263933, upload-time = "2026-08-06T14:22:20.441Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e1/b77ac4ac94e39c28fc3fec6219bde94af247383be25ca32dd1d0cb93e9c3/pydantic_core-2.48.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0e1f53098ae6af238c5f1db3e25adc967625cbd0fdf5b05bf0034367d996d01", size = 2285583, upload-time = "2026-08-06T14:22:22.218Z" }, + { url = "https://files.pythonhosted.org/packages/74/fd/620e2fb7cc04c07ffd32fe0ed48bb28216d7d281b429ec3d36dbda3a7bde/pydantic_core-2.48.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4841c9fbb551c273cb5459526fdd296e30ae585b2b6a50bc4b1364b7df9707e", size = 2078726, upload-time = "2026-08-06T14:22:24.027Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6a/13765d191358b39ad8209763a4fddeefb4ea85caa1ea6540afc2a979d43d/pydantic_core-2.48.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:ab0f7e68c5b8776dd47bbc383d8b80a2362bcc193b7d1e5b10589415709d92cc", size = 2148951, upload-time = "2026-08-06T14:22:25.704Z" }, + { url = "https://files.pythonhosted.org/packages/f1/56/40490cf925b0fbb47f5791498d17e7ed5362e5ad118209e52f4db7e354fc/pydantic_core-2.48.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:614a50093ce079ec2b381ef9b8ff10354eb420ee7b29df21b40e653bab9390b6", size = 2208112, upload-time = "2026-08-06T14:22:27.451Z" }, + { url = "https://files.pythonhosted.org/packages/cf/66/1a7d6028e0f0dfcdbcd9e7f80b16522eef700c5b9189d814ef0c60ef651c/pydantic_core-2.48.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:096519257817ce2ca02dd1cda1c7b48e936efa6a55358b592dce771312f07a0d", size = 2192304, upload-time = "2026-08-06T14:22:29.082Z" }, + { url = "https://files.pythonhosted.org/packages/6a/76/8a723bc5f559148e9228107f599cb6112bf39e8d4c3bb74d46faced76ce0/pydantic_core-2.48.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:a54e4802cebf85f95a22807450e0b23363af2fb815f1749343e47799eee56045", size = 2352161, upload-time = "2026-08-06T14:22:30.918Z" }, + { url = "https://files.pythonhosted.org/packages/38/1e/f2dccd9facee4f792bd81407291c791581d3836fb1367c4821c24da20a64/pydantic_core-2.48.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9199c71f1ea4697edad7ad4712baff7988b4a69ff386a50b38e30b0d85755fc5", size = 2372575, upload-time = "2026-08-06T14:22:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/83/95/11cc28cfadba4d1e7d2df32fb8f74d09353b07a75e89a776c3c05b6f7eb1/pydantic_core-2.48.0-cp311-cp311-win32.whl", hash = "sha256:3b3a39816174eaabc092282d5501dffed4ab6f07e8ce9f866af171926f19e308", size = 1990261, upload-time = "2026-08-06T14:22:34.468Z" }, + { url = "https://files.pythonhosted.org/packages/c5/43/5f6ece1d2cd663f5ab3e85f55006d274fbfd0cac232e29806c018929012d/pydantic_core-2.48.0-cp311-cp311-win_amd64.whl", hash = "sha256:e7431b7327c3875504abeb0f1fcd699f8c5b1b982345c6cba792546b65cee4c8", size = 2080121, upload-time = "2026-08-06T14:22:36.505Z" }, + { url = "https://files.pythonhosted.org/packages/8b/53/b59442e9c6a675e50082bbf77d7264c0487f6a9756b74d6b50c57d5ed29b/pydantic_core-2.48.0-cp311-cp311-win_arm64.whl", hash = "sha256:186e24f569522570480f97b9372bef575875a35c4910ab4e1f31806bcefd93d6", size = 2063057, upload-time = "2026-08-06T14:22:38.255Z" }, + { url = "https://files.pythonhosted.org/packages/47/14/cb52ef72b8290fb9ac7fc8e5feeb20d276dc4f7f5b759af929f8ebc5ce93/pydantic_core-2.48.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:86ab2c8185deea63d9e82406bfd82a8255ae82eaf8cfe8d30caede4b9859be2c", size = 2123088, upload-time = "2026-08-06T14:22:39.953Z" }, + { url = "https://files.pythonhosted.org/packages/89/91/d5f0c8048e8b7b5fa0cd0ba9dc82b0ae9bc342a61ecb1eb0908a9fadb636/pydantic_core-2.48.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:998fe237ad91f8b6a216a344a70cc566557b3c905c4c12c319426425476ddc5a", size = 1935770, upload-time = "2026-08-06T14:22:41.704Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/6d3edb8eca798320eae8d6ddec34c95cf2a6f845a120d2dff984ad62a82b/pydantic_core-2.48.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0692d545312b1849111d7a801515dba648cc1d40f4d1a8c3c7355c92736b0af3", size = 1956857, upload-time = "2026-08-06T14:22:43.473Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6e/35938d2e67dbd903c98252f69bab4d597160a472c90db5e10f96dacabe6f/pydantic_core-2.48.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6f3f7ab3e97aa6d3d659043391b38c3cf41812c93b49692ed969d5beb66ceb29", size = 2077528, upload-time = "2026-08-06T14:22:45.916Z" }, + { url = "https://files.pythonhosted.org/packages/9f/18/3d05c3ef8f91d755bfa7d8403186b2aad1b50ab298d19e58bebaa2d8ddff/pydantic_core-2.48.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e918f1ef44f350b3c03570b1f9b0f2f97e7dfc843fd295951af3db6df6e639d", size = 2260659, upload-time = "2026-08-06T14:22:47.728Z" }, + { url = "https://files.pythonhosted.org/packages/86/17/557007c55d9991600c2ef08200f5f2635307c5445a94ad7bdb9b83c48f30/pydantic_core-2.48.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8198d74ccef5e243c3662bf7b9f847d4afec93bd08d5db8ef1a16dadaec71a5a", size = 2313283, upload-time = "2026-08-06T14:22:49.915Z" }, + { url = "https://files.pythonhosted.org/packages/cc/7e/d14ffe43dd645221285b8bb989af5920f01dc1ff91d0a696724c838efc56/pydantic_core-2.48.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3463fb9e857362b896f345f6c4349d20b817865a05eb570bf838d7c406536412", size = 2074576, upload-time = "2026-08-06T14:22:51.798Z" }, + { url = "https://files.pythonhosted.org/packages/12/ff/c2dc71559d85f8690ada56a5f72ed4db276f8a478dc7b79c6bf72ccb0a10/pydantic_core-2.48.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:7156d0bcc38c8084291b613696b3d23d24e4b8dc7a7a1070c5baa5907936e531", size = 2152359, upload-time = "2026-08-06T14:22:53.637Z" }, + { url = "https://files.pythonhosted.org/packages/d7/9a/ae461c64f7d4dfc6c84f837fccde9b7a469c6d9a309ff09c6e03b942a1ad/pydantic_core-2.48.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3af1e349ecf55a66c53be2fec30fc5a528664ad3d0434aaef5b7b8475fdbd9a", size = 2200931, upload-time = "2026-08-06T14:22:55.513Z" }, + { url = "https://files.pythonhosted.org/packages/8b/40/2c2339379151004f17d6d7ab7f0dbcc360586673f9a4414bcee58170b7b4/pydantic_core-2.48.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:794aab60bbb5a92bb856961b294e149140d81cdf364206c55832d32588137b3b", size = 2197027, upload-time = "2026-08-06T14:22:58.279Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/637fc0e1f1fc4d02b9034e58cc6ed2f937677b82fb292dae7b6f45759ba8/pydantic_core-2.48.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:a52f3eafe91530b9b4b4016a20aabfce4d456be837d2ea7186eaf23ee3320d72", size = 2353748, upload-time = "2026-08-06T14:23:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5a/2144f79041c73cf5b5716434dfa2cf1a4395e68b3dbd9cc01324b4ccb4a4/pydantic_core-2.48.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2cff5642d8c477a79f7b7d05b77620d860cf80e4a0d1cdff72e08c598c12dd3a", size = 2385528, upload-time = "2026-08-06T14:23:02.329Z" }, + { url = "https://files.pythonhosted.org/packages/71/5d/b46f81fc81924c57aa31d11b6dc1ab4bbbfae17e931fdb472869c2d577ed/pydantic_core-2.48.0-cp312-cp312-win32.whl", hash = "sha256:c967fab606fc0cccea553c783a47136333bbc0bc27ed4c401460f2cdf4341147", size = 1972085, upload-time = "2026-08-06T14:23:04.07Z" }, + { url = "https://files.pythonhosted.org/packages/91/e0/de0a9390688d2f1b72d193607772693e25302d0d06525dc0e8ca34752bdc/pydantic_core-2.48.0-cp312-cp312-win_amd64.whl", hash = "sha256:403c28347ac5a0fb4b96b52dea1f5c23ce4515a67c9dc1232ffdc31b88988c3c", size = 2072135, upload-time = "2026-08-06T14:23:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/e4baad10e93749209c32adb04a4e3346d4fb0042229411af6ee72fb46b98/pydantic_core-2.48.0-cp312-cp312-win_arm64.whl", hash = "sha256:f9620adcc4f0aa76b3e2357a9649b45c17aa2834d960f502224c8477db8a3261", size = 2043455, upload-time = "2026-08-06T14:23:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/83/ba/da6251d26ab569e7c87a3acfa0528af3e0ba0bb49ddcd27183d6f1257d97/pydantic_core-2.48.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:742e93d9bcb63d9e5a0fb26e82f175ebf183bc861f8ee18b2b5d7ed09d831803", size = 2122895, upload-time = "2026-08-06T14:23:09.786Z" }, + { url = "https://files.pythonhosted.org/packages/a6/29/70787c54ff498d961d02432cdb343dfe5b59574b22fe5887240a7cdff095/pydantic_core-2.48.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:482c3707518a7163c8148686cd18d27c8f11f89cafa43a6834b42505d266b117", size = 1948678, upload-time = "2026-08-06T14:23:11.503Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/debee51c5ef9161093fb1334e4a70728ee244db32952f4cb1c57d24a6de8/pydantic_core-2.48.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73a5e0f879c9fee3b94473f92860119b6c87779214e8545a1334f346ce5adc2e", size = 1957645, upload-time = "2026-08-06T14:23:13.48Z" }, + { url = "https://files.pythonhosted.org/packages/66/e7/55e5f27b74401e070c9eb1e2a4356864207896aa997c25e0b895574de8d8/pydantic_core-2.48.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8fde25b657fc1d960ab48f9282f6d01bcbe691a80f67aaa08055cf0842e35d5c", size = 2076704, upload-time = "2026-08-06T14:23:15.525Z" }, + { url = "https://files.pythonhosted.org/packages/19/af/3704e32aa527920e861d9f632dc080e2316a3cd9c57706e5e2c293d0fefc/pydantic_core-2.48.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b630303e2c555395d633c699c7b51b7a3428cee40382b1409698e0feec35f519", size = 2257147, upload-time = "2026-08-06T14:23:17.413Z" }, + { url = "https://files.pythonhosted.org/packages/b8/85/440205d22bbcdbd0b8dc8eeb16ecde5cddf6350db0efc0301d52d311a151/pydantic_core-2.48.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2378f8e2f3cf79c449dce50d203111673b2f229769845338f40ae7cfce4b5693", size = 2312925, upload-time = "2026-08-06T14:23:19.405Z" }, + { url = "https://files.pythonhosted.org/packages/4d/85/a5b36fe37e1cba2f0576d746b3b9c5385f6883d448a7cad548146432e135/pydantic_core-2.48.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a9b575ed1193508c0a2226946e5cb4e091528aa7756100355773e86f3c2d9a3", size = 2091934, upload-time = "2026-08-06T14:23:21.431Z" }, + { url = "https://files.pythonhosted.org/packages/79/0f/5a0acc962c16c30b257a27e84062a99aeac568c1558e32107753d39cdb0e/pydantic_core-2.48.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:1a9f44fac958777c1743c4a239d156c12b85e05c1cc4a040d9b227459bdb19e6", size = 2151214, upload-time = "2026-08-06T14:23:23.62Z" }, + { url = "https://files.pythonhosted.org/packages/e0/69/35c42e00c5b0d1e94ec4ab808381c51a35952d65ea936da68eff40e14a8f/pydantic_core-2.48.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fad9aabe217d8a62cd1c46afa6b48134da8e5f6eb148bb228fac5886b9a40407", size = 2198942, upload-time = "2026-08-06T14:23:26.088Z" }, + { url = "https://files.pythonhosted.org/packages/d6/3b/1cffc72b05551d9ac6acbaa361a42eaa5d740d2cbd75c881970de2055d2d/pydantic_core-2.48.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:ddea29d75d8c65dc6c2b3e8b9a178eb27e0093ded129f5bdf0b7c6654dbf90e1", size = 2196306, upload-time = "2026-08-06T14:23:28.275Z" }, + { url = "https://files.pythonhosted.org/packages/23/0e/cd1d186737a45d05d169db907168ca55aa23ee3f2f84fe85355ce9f9c6a8/pydantic_core-2.48.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:85c1006a8ae5b5ddfd908cdc98b01db9c0eb3a012c116e9054f66d4f9a6dd210", size = 2352832, upload-time = "2026-08-06T14:23:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9f/8c3fa0ceb5dd5336c193414494009f141a78a0fe47145ceadfa909e28c5a/pydantic_core-2.48.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:94ed5b7ac4522a462aa44a3e830abb2ea18c1c6fc633d9fef222f63ae12d610a", size = 2385571, upload-time = "2026-08-06T14:23:32.758Z" }, + { url = "https://files.pythonhosted.org/packages/99/2f/51fad23abd315ab0c6d91da7f4fba8e74b217e194352df076115b7b69665/pydantic_core-2.48.0-cp313-cp313-win32.whl", hash = "sha256:52a860e1f01bced6685d016881338fc74d5beb491aed35a08f64d929d7c894a0", size = 1971015, upload-time = "2026-08-06T14:23:34.864Z" }, + { url = "https://files.pythonhosted.org/packages/c8/85/98ece6458b7fc7cf22dd546d41f0341bff4a049076d809187a0717be811f/pydantic_core-2.48.0-cp313-cp313-win_amd64.whl", hash = "sha256:29f6cf2dfff9206a949eeba1de29578e2199c078d3f2e25f47253e15da44fd4e", size = 2070829, upload-time = "2026-08-06T14:23:37.137Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ab/a72219854be7de4b1080f6946d9a11759ecedde770a28b94460f668f5c60/pydantic_core-2.48.0-cp313-cp313-win_arm64.whl", hash = "sha256:41eaed1185feacdd39cfa02fbb46ea3e15493cedd70a901c6a1aa437cf1afec4", size = 2043289, upload-time = "2026-08-06T14:23:39.24Z" }, + { url = "https://files.pythonhosted.org/packages/52/b7/e486a048aee5eabc7c8581bd795115386b20ab89c48e15a66f582ef2c393/pydantic_core-2.48.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:34fdd956d95ea79cbd58dd29afc7ebf658d32ec20b738a5a9110eac3dcc42a66", size = 2124317, upload-time = "2026-08-06T14:23:41.31Z" }, + { url = "https://files.pythonhosted.org/packages/6d/7b/282025d5305940401cbe18c5b04a27c1790be9d67713653c03ccaddda0d8/pydantic_core-2.48.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:aead7fc064c84c0a57f57ea373b971d33ae0df05b1866c669d7b71c0083b00f2", size = 1936187, upload-time = "2026-08-06T14:23:43.332Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9f/f2395f0addbc578b22ff8352ff888237f00606fa127ede6ecd1658cbcf9f/pydantic_core-2.48.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e94dc01965be40ccf1fa71833fd85d7df588a49b64a929b2d5178208a895ddb", size = 1959223, upload-time = "2026-08-06T14:23:45.376Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/350c3f81ad4c698fb800c1c110884c963a420c9c5ca7a8ff860e6d56d22d/pydantic_core-2.48.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9244be8b15f2edd25e8724b91f0ef406a73fef29a3ba9cdf3a19acc04c135613", size = 2076795, upload-time = "2026-08-06T14:23:47.792Z" }, + { url = "https://files.pythonhosted.org/packages/56/a6/7036bda94ed740ad44174dec7875b299c55c1b0842f079227fcb3880e11f/pydantic_core-2.48.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e615698372a8c93f91fae46ee23dc93c4cc52ba97662d2fa31829e63892d744", size = 2259477, upload-time = "2026-08-06T14:23:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/85/61/c13e8bb5b926488fe65a6f371fab85d69bdc76fb397ee81f3b7f5d7c14f4/pydantic_core-2.48.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f2dccc4c7b4c50854ffe2786c192a7a4521d851fd42dcb3ca697bcebe0dff6e", size = 2320484, upload-time = "2026-08-06T14:23:52.29Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/192c011f9b4f62df3f620af239289e35dee9205299ad03e3a351de4dc3cc/pydantic_core-2.48.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6643b3df515bb47c8e46c07f4530cdc7f52ffcc1845d437011dbb9198200148a", size = 2078103, upload-time = "2026-08-06T14:23:54.458Z" }, + { url = "https://files.pythonhosted.org/packages/8d/8c/a40773b3f9876f32d4a9e91686f31f83949694381f32baf58e4e6d9469d7/pydantic_core-2.48.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:612c4541a52fc9173df90d62cbd931a2c2f87b9f2eebe5e71b2f2d89410ee6b6", size = 2151310, upload-time = "2026-08-06T14:23:56.551Z" }, + { url = "https://files.pythonhosted.org/packages/1c/8f/b9cc3e09e8c80c6b4f86eee714d615344700fe8d8a8fe8a39fa013406d3e/pydantic_core-2.48.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6cbb4d9f81063600c13b86a45b9413a2c4148ddfe534108a57dad47c72377f27", size = 2201859, upload-time = "2026-08-06T14:23:59.042Z" }, + { url = "https://files.pythonhosted.org/packages/96/c2/7515c394fc5990e4469001ab545637c3861b959377cf4ea7caa2e577302d/pydantic_core-2.48.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:43bec89e5624b0a3b9f765cb7c2f62959547362c48d82471f4da34af00612ac7", size = 2196170, upload-time = "2026-08-06T14:24:01.261Z" }, + { url = "https://files.pythonhosted.org/packages/48/62/577b912824f3efbe20750e54e77ae6e0d6d964ab002a54af9b7aa12e3e3e/pydantic_core-2.48.0-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9906053654da2b5270ffe3699868b5bb3f39b4cb74b085cf6cbbde329094102c", size = 2353582, upload-time = "2026-08-06T14:24:03.695Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e4/8da652a5be351c66864700556a60f1e0092defb6c7bf8156c63e757db40f/pydantic_core-2.48.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:4dfc6b534178202a3e43e88d73fdf5c85c151df8e920c68ea9d4890fd212cada", size = 2387345, upload-time = "2026-08-06T14:24:06.876Z" }, + { url = "https://files.pythonhosted.org/packages/19/70/b7b9042d5e745d3893f8b5597a00727325d0f41dd5e5fd8875335d876770/pydantic_core-2.48.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:4fc45a49334c54541cbc97bf416d9300b4b1d3b2840dfb079b1123dc3f9ef5a6", size = 1403190, upload-time = "2026-08-06T14:24:09.012Z" }, + { url = "https://files.pythonhosted.org/packages/f7/2f/4bb02fddcaeb4255d5c7d165479ffb4657600f180aa836cbe9f7ef7a16cb/pydantic_core-2.48.0-cp314-cp314-win32.whl", hash = "sha256:def7b0c70a95a9348fcfbedf7dba9c97328b422c38ee894b8b0bb48663e408cd", size = 1972768, upload-time = "2026-08-06T14:24:11.404Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ea/24f620aad5d4e193a9e9644337983c0db086cd9d49c35fa9dec1b658031c/pydantic_core-2.48.0-cp314-cp314-win_amd64.whl", hash = "sha256:2eed50d5dcd57e319e88e117294815eae1c65d2eef316d7d87eb5398d649833d", size = 2072285, upload-time = "2026-08-06T14:24:13.846Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/a987c7ff8028af469e845113049d7e5bf39c98a88bb24dc2db0637c875e8/pydantic_core-2.48.0-cp314-cp314-win_arm64.whl", hash = "sha256:69ebfa782ca474ed305227a04d370c9d7f5a7f5039e7802505145d3e8ad8e309", size = 2039011, upload-time = "2026-08-06T14:24:16.183Z" }, + { url = "https://files.pythonhosted.org/packages/46/60/f02acd0a97866449877bb220b0df8241b667b385236379613bfcbdf96116/pydantic_core-2.48.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:1aa7dfad60b42bbaa6b7be7e28f386be9b742483bfeaf26d310b466092f271be", size = 2125136, upload-time = "2026-08-06T14:24:18.54Z" }, + { url = "https://files.pythonhosted.org/packages/a8/7e/a7e969d6c8a31a3bc18bc7a18f64aea8d62635dbb7e3f72c36f7638c221f/pydantic_core-2.48.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:75df20fe8ddab1153017cd791c965674b509b6fafbdf5aaef2e6793a65715694", size = 1914888, upload-time = "2026-08-06T14:24:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/b5/60/c5feb7147483668b9dabd9b66fa530f012d168ce28ee75cb1b1cfc577467/pydantic_core-2.48.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dc59ad81e0e1c3209c2040e24ec99a772155f7dcc62b09829422d4bc5ad3133", size = 1939973, upload-time = "2026-08-06T14:24:23.263Z" }, + { url = "https://files.pythonhosted.org/packages/23/40/6451ddf9dd1853993128b50bf5501b788105d678a402b2f468f4ca7082a3/pydantic_core-2.48.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9cf1ad7d2d3793bd15843180da3f29d1d475661f70788a696c0faac8abc1dffb", size = 2069026, upload-time = "2026-08-06T14:24:25.628Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4d/153ca46ffe938ff4c1b020a3f2d1117e11e8e3c9ce4151aec217b4670f0f/pydantic_core-2.48.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e94566a1c7eb35b9494ad72e3bfce0ef9bff451aa883ce7b73083ac0e05b572f", size = 2264691, upload-time = "2026-08-06T14:24:28.227Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/b69f11e3f162e64dd90cbec4fd5234979600391bf8a46ec9cf5ed4d9a4bc/pydantic_core-2.48.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:643332ae4b638daaa4130089ea32039dbbc9883cc21c555771cb12a3b10f392a", size = 2313963, upload-time = "2026-08-06T14:24:30.433Z" }, + { url = "https://files.pythonhosted.org/packages/84/b2/f5f329d2caa9b397afd89c861ce17d9d95a79218b0949f03597d95106c6e/pydantic_core-2.48.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c950716666760f5b5bb4c115f1466adb9b6a117d28c4a0489049e258a88d01", size = 2070202, upload-time = "2026-08-06T14:24:32.857Z" }, + { url = "https://files.pythonhosted.org/packages/ef/38/96c7017753e4d0ece819c5c245aefa03836e930b6785e8ada6389fb06ace/pydantic_core-2.48.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:fb7f8a79ac0c4fbd0849ac2431106659c0e2c3cfbfdd18810f2305a05661398b", size = 2140157, upload-time = "2026-08-06T14:24:35.614Z" }, + { url = "https://files.pythonhosted.org/packages/14/9c/8c43df859baffee901b02380972c2f14312a30759987c6e5da8f2efc1dde/pydantic_core-2.48.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:782e344102a11e25be4f416d9f14f0cc2710f255339ed95b0d1fb026892cca47", size = 2195094, upload-time = "2026-08-06T14:24:38.04Z" }, + { url = "https://files.pythonhosted.org/packages/a6/27/b29c4a3de121f09d935c95813b3d302bfab338b3ccf9e42944a409151d9d/pydantic_core-2.48.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:5619f21f760a7dfa6fd24f7d73b15d47c7327114d43f683791244a9b7e9127c9", size = 2190993, upload-time = "2026-08-06T14:24:40.581Z" }, + { url = "https://files.pythonhosted.org/packages/0d/70/35979c7fed1ea473571017a346f2d1cd592b2f646c6393a2a453b54018e5/pydantic_core-2.48.0-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:51ac6763fb9043d00343ffa11c1c46f06d73ce4f3491e64d82729519dec4489f", size = 2345602, upload-time = "2026-08-06T14:24:43.093Z" }, + { url = "https://files.pythonhosted.org/packages/b4/54/de9a3b21464f1917719a7c82801f1c5a27840ff9479b0379aaf136f02735/pydantic_core-2.48.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:c066ec851c4a742f7053e615a25794c30c4304f6ab1f6f8b7eb69e06a23758ec", size = 2392353, upload-time = "2026-08-06T14:24:45.615Z" }, + { url = "https://files.pythonhosted.org/packages/c0/92/de6405049ec6ba57419c7a893869d19f92e436fab06b028dc0d170384fb0/pydantic_core-2.48.0-cp314-cp314t-win32.whl", hash = "sha256:9310814fc611a1d40875c8f07cd4dcd156202d0fc2d9210fbf1cb4d130575e0d", size = 1960156, upload-time = "2026-08-06T14:24:48.022Z" }, + { url = "https://files.pythonhosted.org/packages/ad/22/50f6e4b1125859118a2f9604ebc74b96ad16d730d5823b369f4bc4432450/pydantic_core-2.48.0-cp314-cp314t-win_amd64.whl", hash = "sha256:54bdb4c47e063c109722fb723da0a306414700cde573ea59a6f0dd620d434f67", size = 2054413, upload-time = "2026-08-06T14:24:50.546Z" }, + { url = "https://files.pythonhosted.org/packages/2b/fc/2969e40c70d64f0c6e27e0586aa1449d7cb432ac619fe19831b2b52906bc/pydantic_core-2.48.0-cp314-cp314t-win_arm64.whl", hash = "sha256:157e1f7ad13864127d12b5909e2c3b02cc64ae041bd3fa9239ab292aff4ddfd1", size = 2051246, upload-time = "2026-08-06T14:24:52.887Z" }, + { url = "https://files.pythonhosted.org/packages/35/c2/c9c1723484f23e1dba7144b51abead1983a0e88a1d2d2edaf652c19f2833/pydantic_core-2.48.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:951d5f7754eb81381eae252af74f6c751b954b1f36e4e7d3b3b1f7a020ce0c6a", size = 2124365, upload-time = "2026-08-06T14:24:55.75Z" }, + { url = "https://files.pythonhosted.org/packages/97/4f/1d771e5d910fa172991615db3d850d68bc2a18b0fa33e56c9e04ff6d32fc/pydantic_core-2.48.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:c0f553fb3ace879b900e217285f83ab39bccdd0bf91238670b20b825d801037c", size = 1948433, upload-time = "2026-08-06T14:24:58.478Z" }, + { url = "https://files.pythonhosted.org/packages/05/9b/0566b0ab987cbf0648a8fb2386d1fcba1ccf2cdb3a10abb91b0953c13efb/pydantic_core-2.48.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd33c885e553a5bd83a210b341026817374dc192cf62bd169b969d732a155cd1", size = 1959601, upload-time = "2026-08-06T14:25:01.061Z" }, + { url = "https://files.pythonhosted.org/packages/16/b7/28fae984c468b46f5d38d9dc6f7579257aa155bb0ea1d4e4ed3d890535ce/pydantic_core-2.48.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f6b45257e2682fc2ea42d2ffeb1fb92630329a496214c2c438523f9b614cbc3", size = 2076937, upload-time = "2026-08-06T14:25:03.706Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/6f42bc05bd72a24885a19c6fdcf89891fd3588f719438110a0c7dbef84bc/pydantic_core-2.48.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc088260cc20d901d5c380e9c881ceb644cdb9c69580674e1c25a38d4417eb5b", size = 2260902, upload-time = "2026-08-06T14:25:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/39/56/033f39acd30fb63f650b1a004f04215d4e1a266d6c04eb390138a1e6f449/pydantic_core-2.48.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0f144e303ad0eaf41fd596a78a7706542eb97ba81c13c8fd51c2af5233b73590", size = 2320569, upload-time = "2026-08-06T14:25:08.718Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5b/c81a249735b046e028f4287d85a08f5ed0eae86475c33bf6220225adb99b/pydantic_core-2.48.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd51d8521aa76356350f05951e45a9bd05a342e31c5d3ec437bfb5d327ade14d", size = 2078719, upload-time = "2026-08-06T14:25:11.247Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1f/1de5b10bdbfc48bfdd8195b6e0a2747cd0d549aff317894bcffbaac835ad/pydantic_core-2.48.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:b1f382f0e0ec8623ea4f3f477ab22e67d6ba684e25f49a39a01b121dcf528bf3", size = 2151398, upload-time = "2026-08-06T14:25:14.192Z" }, + { url = "https://files.pythonhosted.org/packages/52/cd/ccdb8ee14e985b5acc7e5aae2c2e3a2afdf29429fb4d5dfe19c11f5adedf/pydantic_core-2.48.0-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:69c17bce5458c97b1c34d2ea2b388de6e27285d18d8b5eb14195d7de5dded37e", size = 2202063, upload-time = "2026-08-06T14:25:17.247Z" }, + { url = "https://files.pythonhosted.org/packages/64/f8/1cf0b2a3814492ebc4666bc104d4a451a1559e1f6c1b9b649bab220deab0/pydantic_core-2.48.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:a9e540e17c61ecadc2825cfe097efe1e618f67613c331ff6e35a30f7d0d71180", size = 2196335, upload-time = "2026-08-06T14:25:19.879Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d4/9eb451774f8a643c880e825540a8678d9f6fed0905a51ab879de95c750c5/pydantic_core-2.48.0-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:729e27d5877d3a61f829d43042293ace2b725c1e66f367a187c05d11280dab83", size = 2353940, upload-time = "2026-08-06T14:25:22.477Z" }, + { url = "https://files.pythonhosted.org/packages/8a/79/2a25a6d20dd2f99d95477573b512884557c435c6b08ed58c1b4203c1eb85/pydantic_core-2.48.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:83785d4c37e362ab1efdaf4067cd4a2ee1f7cc380ac7a7fad04a9542c697027f", size = 2387375, upload-time = "2026-08-06T14:25:25.307Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bb/1ddac2399ad29413d2ab3ac9f12e0ea9b584122b7a0aa6716e2ebdbe67c9/pydantic_core-2.48.0-cp315-cp315-win32.whl", hash = "sha256:2ac14fb788fc51fb2cd044817bab9c98abd352fe3e4424463da805d18a0478fb", size = 1972536, upload-time = "2026-08-06T14:25:27.974Z" }, + { url = "https://files.pythonhosted.org/packages/c4/89/5136d40f49ac486e35fc8295f28f6e42c6e891b974004c814e589f586f9b/pydantic_core-2.48.0-cp315-cp315-win_amd64.whl", hash = "sha256:1778e349e1bcaf0b1850d4b9cf0a1ff4edea36bc11b69b560c6ef644e60a9e85", size = 2072192, upload-time = "2026-08-06T14:25:30.627Z" }, + { url = "https://files.pythonhosted.org/packages/35/62/c4fb6b7ac6c1e7420232b699b8b553bd48edca2c999b0aa03d9a2de62752/pydantic_core-2.48.0-cp315-cp315-win_arm64.whl", hash = "sha256:eaa9e2d0ef1bb345d6870f72ab12b2a5522231e38bfaddc0b903b5af00537267", size = 2039143, upload-time = "2026-08-06T14:25:33.373Z" }, + { url = "https://files.pythonhosted.org/packages/3e/fa/4afd93d028d7c6e3da4caa7536a02d4934b41a289a4605715cb1ccd889f3/pydantic_core-2.48.0-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:4e36b7ba6c7713d17d768b21e06763cb6440eaf3798cec4ac22e4c685c73d6cb", size = 2125205, upload-time = "2026-08-06T14:25:35.933Z" }, + { url = "https://files.pythonhosted.org/packages/b3/28/6eaa4ebbdba7abfa546624c5e1df33e1d046e4e04d3ded053792edb0b15f/pydantic_core-2.48.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bb833596eced78da91d24e7d84150085aaca21825e2267f098c972f4a790a1a6", size = 1914987, upload-time = "2026-08-06T14:25:38.79Z" }, + { url = "https://files.pythonhosted.org/packages/e5/04/0a5be1a9ca5b4448d58a2969e576fd4f7dc1514bdb818b8367d5b9f4169a/pydantic_core-2.48.0-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de1c807c15fd6403ef651c5ae6324a640d92cec6b39a5af58c57f4e031f05859", size = 1939994, upload-time = "2026-08-06T14:25:41.521Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/d70810f2406ef8aeefcad7c79d93ab70ff854749304c911388be25b094e7/pydantic_core-2.48.0-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2216aae7b54dc12c8f6228dc2ca92dc758f4f8906f2fb5565063e2499361b910", size = 2069282, upload-time = "2026-08-06T14:25:44.157Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f4/8c8e07f4009e828a115d500999727194e30e854f2cc4c8977025255e3050/pydantic_core-2.48.0-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:837426357feb1f52c42b24e611728b394a6f4ae4ac7c461ed55be201bcbc811f", size = 2264913, upload-time = "2026-08-06T14:25:46.866Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ed/a2e36d8aa2feb98eccac2641c07945cfd58c81656831986c7973cb6b5ecb/pydantic_core-2.48.0-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb8807da40e346c5ae65846e721d96ef84776e103b5ad6923a95843ba0e17eac", size = 2313972, upload-time = "2026-08-06T14:25:49.664Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e8/106c6f135e260f884786e95655b8347a41131f1a2ce807fc2611ee7800b8/pydantic_core-2.48.0-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55bb2a177d33bbf22e441f34928183230018518578360cd782bbe17e3a877b5e", size = 2071006, upload-time = "2026-08-06T14:25:52.338Z" }, + { url = "https://files.pythonhosted.org/packages/39/8e/19ed0f6e36047ff6fa04990c50d0f031f3b8f54168f1671c34dc0454d636/pydantic_core-2.48.0-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:d77765d9c9bc37edc2ffb98e724a3e57b08932bef5407ba04f00bc59854af113", size = 2140081, upload-time = "2026-08-06T14:25:55.428Z" }, + { url = "https://files.pythonhosted.org/packages/69/43/40fb548caa91b673ba479b4649a45a96875391b5a5ed8769d943e23c3324/pydantic_core-2.48.0-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d058233dba56eb3de2eae96cb0acacc521417ed34de72507c9360202cef32ace", size = 2195173, upload-time = "2026-08-06T14:25:58.361Z" }, + { url = "https://files.pythonhosted.org/packages/31/21/6094939bac24f2b61ca8b994fc8560050656a3828d85e9563b5b6ffba97c/pydantic_core-2.48.0-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:95c0e79985e0c1af0f769384f369412652f06e2e3d5a679223d15820f4881107", size = 2191111, upload-time = "2026-08-06T14:26:01.213Z" }, + { url = "https://files.pythonhosted.org/packages/70/46/7043d35aa1f061ecc1950cd690e559292854d2e300b197c3adf865c49797/pydantic_core-2.48.0-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:2384954580b564a3ae0ffc8bf37c794c75c22dbb3cac0a9f941b9ad1de3e4e03", size = 2345837, upload-time = "2026-08-06T14:26:04.226Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b8/d9d42bdc1a1e45864ffbca118c08f8fec57c736f4b89f93f28282990bb34/pydantic_core-2.48.0-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:c40ba3924215e6c1616e897dc4342942db38c0f21a3bf50d7f6adfe3d9de344c", size = 2392376, upload-time = "2026-08-06T14:26:06.917Z" }, + { url = "https://files.pythonhosted.org/packages/d4/b0/807114c79eb12cd56bf484ac3e0fe584401b7783461c3e12250d9a47dafc/pydantic_core-2.48.0-cp315-cp315t-win32.whl", hash = "sha256:e88a758fc73c5e5e031c33b11f114e112ec0d5b5fa537db7d8d5e8c44c3c1841", size = 1960339, upload-time = "2026-08-06T14:26:09.755Z" }, + { url = "https://files.pythonhosted.org/packages/60/1e/b1104f7ccc1de5be4e2dab5e4dbc3ac92e5b34b902dace37e1308d9d539b/pydantic_core-2.48.0-cp315-cp315t-win_amd64.whl", hash = "sha256:1992edde312b15554d048176de39d7dbd59b3d637a96d71457e5251d089f7662", size = 2054666, upload-time = "2026-08-06T14:26:12.528Z" }, + { url = "https://files.pythonhosted.org/packages/96/0e/60228794a20a6b77ad09e3c6018a85206575828b5aa73782846bc1c68272/pydantic_core-2.48.0-cp315-cp315t-win_arm64.whl", hash = "sha256:1514098b2d6d91e94840b90fa801e717893255c7468061faf38728ac27063d55", size = 2051453, upload-time = "2026-08-06T14:26:15.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/79/6f19f515313ea1893cb8e80cdf767efd56f0f9cf1bd6731423bb0e6eca68/pydantic_core-2.48.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:40a035f7a82ffaccd9599617bb0470425328be77e7937efe5e050d5b692a75e3", size = 2060582, upload-time = "2026-08-06T14:26:18.201Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/d69022318577fb290b57d58cc4b0f0c40feb7ede075d063ccbed3059c28e/pydantic_core-2.48.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:10dfc23a238a3385c9a2c0a63ff7ca5ec42adb52c2c1c2f56b4c3d888fa37307", size = 1974878, upload-time = "2026-08-06T14:26:21.169Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/e9a6aa5c5950b25b1e98f560b4b2886469e5c6fa8c14a89510140e63eedb/pydantic_core-2.48.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddfb1090d09ae7fd9e377ae570de535792d7cec088fb7d283ea6ac67fc1f24f0", size = 2005154, upload-time = "2026-08-06T14:26:23.939Z" }, + { url = "https://files.pythonhosted.org/packages/10/79/9d05fc51db5eb6f9033197e4c1dc20d268474b3e9e13124c30bb75cbb2ea/pydantic_core-2.48.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062289bfd4f99f40bab32f725fdcdad9244119c06d41d62da019a3f4827cdc2d", size = 2151417, upload-time = "2026-08-06T14:26:26.766Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5d/cebc9ab997f34848ba5d1815fee3312f0f05d9d0dd2005a43ae1fc9457b1/pydantic_core-2.48.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:36196b58c314af63cb1350f57ebfbed122678f2727e8ad3f41015b9d57e5dc94", size = 2109109, upload-time = "2026-08-06T14:26:29.511Z" }, + { url = "https://files.pythonhosted.org/packages/14/5e/c79c22aa32d57b9a0c97be5efe84ecd42804660d5ac83b142d34361c77b9/pydantic_core-2.48.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:83a9509271a3a1314fc2a928d98b3b9910250e42a9a409b852454a4c6fee4710", size = 1940937, upload-time = "2026-08-06T14:26:32.324Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e4/aab480442be0228415eaacadc511971936ad9b5edab539dd3d9c69099e2b/pydantic_core-2.48.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f751b64b99cbb1596ae6e9a044610a1dd716f07f65ef4dfcb8693458d66b14", size = 2006022, upload-time = "2026-08-06T14:26:35.108Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b7/5de142e36cfadbc962d223e5ba854b2bfe67958c9ae5a3ba3fbc97adf6ee/pydantic_core-2.48.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f1d556f02ae80aab0626639eb1aed304e7d37e52791839b39d7c7ae0acfbfbd", size = 2154883, upload-time = "2026-08-06T14:26:38.029Z" }, + { url = "https://files.pythonhosted.org/packages/7b/b2/7a5a761bf25fe39feeb34e015b4a3f3b78f7c7eb8c79c893c3583513c8b2/pydantic_core-2.48.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:44ad0cd44c9051b0ba47aee87a4a70ee2339f5b4490428a021cee3627bd9bf8b", size = 2062374, upload-time = "2026-08-06T14:26:41.122Z" }, + { url = "https://files.pythonhosted.org/packages/09/9d/d9487ce14c43ad223b0ece3750afa3a164ab8d25ac7fd811d5c9b46a0802/pydantic_core-2.48.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ed47cb6383542456456bd0166df05cfaa0139c363635cbc74a69976d37218367", size = 1988783, upload-time = "2026-08-06T14:26:44.19Z" }, + { url = "https://files.pythonhosted.org/packages/9f/62/2ba76bd8e3d4bdcbefe9e88e20d60fac3707f00e8d61244a076155470677/pydantic_core-2.48.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b6a6d9fa9d52b2d2097ca4e5a40759b76e0021c0d68b36863d0538c95e1090", size = 2149050, upload-time = "2026-08-06T14:26:46.961Z" }, + { url = "https://files.pythonhosted.org/packages/28/5c/647cfcae2e2d270e13ef5f9746d2450c034d0ea3708172ebd7fc98a9b97f/pydantic_core-2.48.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e022eb9d12dd5b1b1a3a053bcb71e1bb7ef762965c4a18acca8bdedc6f5379bd", size = 2209903, upload-time = "2026-08-06T14:26:49.863Z" }, + { url = "https://files.pythonhosted.org/packages/97/9b/42c947325c7f8704888cffccf180cabe02774cb794a398c4e9291abc01be/pydantic_core-2.48.0-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d7be132e08826f4d8da5c0395c4aa6b4f6a13bd9f75f1615711c9e75d5dea019", size = 2200811, upload-time = "2026-08-06T14:26:53.138Z" }, + { url = "https://files.pythonhosted.org/packages/10/7f/defc755e4a62b7e48eb66c41066433408117fdb7d94188b9d3e1120f6346/pydantic_core-2.48.0-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:bc86b6a4a7f1f6f6e51841da08997cc37186a701ea0788810ef61e3c79fb6d29", size = 2352445, upload-time = "2026-08-06T14:26:55.933Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5d/0ae5fc91a4292e101b8aa8e5a79643dc7350d3383a6c60c3918b5732f768/pydantic_core-2.48.0-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:93072e2104eec33679477b6ba923e90fde59cedf2a525dad869b685c640b44f0", size = 2375199, upload-time = "2026-08-06T14:26:59.666Z" }, + { url = "https://files.pythonhosted.org/packages/f5/93/466da76f70f7cff08ac713ca1cc4e5f9ffe5de6aaae4b885c723c33a70f0/pydantic_core-2.48.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:19b9d3bb1a2ef6c692729e47f015327d6f9d2387edcc36fa2f57851e98166cb7", size = 2210048, upload-time = "2026-08-06T14:27:02.612Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyopenssl" +version = "26.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/e8/7325d258199b159eb2c03fe32107533e2832e70e63f4fb88a6aa00023201/pyopenssl-26.4.0.tar.gz", hash = "sha256:28dfcce0162b9211413e26dfbfdf1d24317fbeba18fc93c12400a1856b2a0bc7", size = 182046, upload-time = "2026-08-01T19:50:50.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/ad/2cf6d3fa2fae5c79e1ed9960c0d42badd0f94d81dd12b50604cdc839e648/pyopenssl-26.4.0-py3-none-any.whl", hash = "sha256:f0eb0cb2d581d3ad2b9c489468485e7f2ab6727d08401bcf9d824c3caddf3c1c", size = 56026, upload-time = "2026-08-01T19:50:48.94Z" }, +] + +[[package]] +name = "pyqwest" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/59/97531fd9d0a06d54e84f5493775739b0c1d0d13ec704974d04fa423a3332/pyqwest-0.9.0.tar.gz", hash = "sha256:514dd0b37d7a1bcb978b5d6423d15f89cf7dcf8058ac2a37aa42cd30090de89e", size = 477462, upload-time = "2026-08-10T01:55:36.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/34/d556fa07d9bff21fc6c9c16738614c6336164b65d9ced1ce9f1c1044aaeb/pyqwest-0.9.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:517d2eb295d56ef1530cebe0cf290a66fa199c57955bd1c5f1dc592240fbb7d6", size = 5224594, upload-time = "2026-08-10T01:54:18.646Z" }, + { url = "https://files.pythonhosted.org/packages/c9/3f/1d7d1a08ebf8838c0c85e99ab5db5c1aae457f76ee03b60c8f4fc4948e01/pyqwest-0.9.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:408dc692835363b6824bee61ee9e4c9a5bba9c08d75b903f82f9a802e41d1b30", size = 5100753, upload-time = "2026-08-10T01:54:20.669Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/22c4056b526baa4c2c953f4e99925ce0bb14e7747d66e6ac3448a34f76e5/pyqwest-0.9.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1abf1ab3a0c1b545651ab37a643acc7909c4e7644a63a2e4af36e3e230a0db5", size = 5619882, upload-time = "2026-08-10T01:54:22.468Z" }, + { url = "https://files.pythonhosted.org/packages/de/2c/8f1159a30bbc905b2fcbc386740a260e76ae64372feb263103438ef5201c/pyqwest-0.9.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b02e5c9ce57e079eb3716c464d3818ad2573d7743483c39526ad77ee95af806", size = 5564744, upload-time = "2026-08-10T01:54:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fd/ff15034d7874fd208d606bd80c924fb0b1091db8159c892672168b656de1/pyqwest-0.9.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:98bdaa30a3a8d8c71506a33c084e485a243fbf51e77130bb39e0f249dd116142", size = 5779373, upload-time = "2026-08-10T01:54:26.753Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b5/eeafdeb65ad8fe9c39044aedcfc97610f4e5c7b5620ca9fe2d504f669108/pyqwest-0.9.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:10eba84f9d8512d23bd16dd60b1c77dab956470361559214981dda065df4ad1a", size = 5973306, upload-time = "2026-08-10T01:54:28.729Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/a8979dab5c591529f858d1505d09b3c8b6dec337921f15bed4858c55716e/pyqwest-0.9.0-cp310-abi3-win_amd64.whl", hash = "sha256:af4e859800b02cd1fcdd898af26a881c4a8ce9520f71f6cb0a5403523d2cbbdf", size = 4834706, upload-time = "2026-08-10T01:54:30.335Z" }, + { url = "https://files.pythonhosted.org/packages/1d/9e/d070ca1ce3c202952770436172889a4b0556a564a490baf726f7bb895eba/pyqwest-0.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:abe322cc1b63947b493bc06028615e0d5de655928159227aab64f60a0eb0e27d", size = 5244745, upload-time = "2026-08-10T01:54:32.257Z" }, + { url = "https://files.pythonhosted.org/packages/41/97/ebded0bb35a79f51bb2d1519e34922d79c8825f9e71b696bb20f387a08c2/pyqwest-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6b2dc6a9c583859d031941e1041b8e711afe0ad4c130628207731cef2b1a486", size = 5101044, upload-time = "2026-08-10T01:54:34.088Z" }, + { url = "https://files.pythonhosted.org/packages/a9/2c/a7f117e793b4e27642807bf7230b9fa297ff1e1ac489fc260fc3d4350ae4/pyqwest-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:551e47714da72a72939958029eb37a754cecd974344471c4a2038583a66a5396", size = 5630841, upload-time = "2026-08-10T01:54:35.885Z" }, + { url = "https://files.pythonhosted.org/packages/b4/b4/be47fc141529941352a848cd77da581803137b733993a51bf464f2c48915/pyqwest-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3be3d38ccdab3077bf1cfe90c346ce927f9b49c56dcdf20e060f5f2502d01021", size = 5572909, upload-time = "2026-08-10T01:54:37.653Z" }, + { url = "https://files.pythonhosted.org/packages/cb/fd/703635e12710ce7ef5e961f5e1b22c609aecc8f07eedd79477b48d1edb74/pyqwest-0.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:45d9d424da878b22604799d1ce2a0a5df054924487ee5a851ad08025e2076e5c", size = 5794049, upload-time = "2026-08-10T01:54:39.363Z" }, + { url = "https://files.pythonhosted.org/packages/3c/15/941aad56b108c743df9d41e124be82ecb6be0eba1007e945b6a39f5dd1a0/pyqwest-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e4420adcd2b756da706b31a04f095a521f6e440dab284b7a1b46c76e048c1f78", size = 5974542, upload-time = "2026-08-10T01:54:41.202Z" }, + { url = "https://files.pythonhosted.org/packages/b7/86/8445fe05b47322047347464a909ae95dba023bf8409b9967f66bda2905ad/pyqwest-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:25734bd9798bbfaa53c83d395649dd9ea9a791a64b9848734955cb78ad52c832", size = 4847034, upload-time = "2026-08-10T01:54:42.899Z" }, + { url = "https://files.pythonhosted.org/packages/a7/92/013fc3c94f0dc4eeca556431797da477d76ad43d69134837ba149468dcc3/pyqwest-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6e4c2afe6c7293d9e42cfdb0489b0b5f64002159c2dc9c45d9322c1b4ffb42cd", size = 5244793, upload-time = "2026-08-10T01:54:44.891Z" }, + { url = "https://files.pythonhosted.org/packages/48/61/c685e0c595f3f1b7842a776c48aaf2b3f47270a61672967d9d38f7c5e7f6/pyqwest-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:67f153b94dfbb9aeabc38f20a1bada1acfd9361bec12db86268bd951be35a154", size = 5100715, upload-time = "2026-08-10T01:54:46.777Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a1/a28b2cd704d7dd37043f1e6b398798576c12293f3efae84f00e7f6a5625c/pyqwest-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9942ffa243c8c2243e3ce04e3e3ec9b4c119346c963fd0051cd989f0defecf6", size = 5628787, upload-time = "2026-08-10T01:54:48.488Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/6255c7f17cd00e39f1c4df3003cea2d2d087e483fb4c49a6535307cd21e0/pyqwest-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f8f9c880c19826fead1838e12b042865b15df3ec844928cc74188a8b740f761", size = 5571946, upload-time = "2026-08-10T01:54:50.181Z" }, + { url = "https://files.pythonhosted.org/packages/37/71/344738bdb38bddbc07cb29e5b1287c08db9e53d6b322581492571bdef748/pyqwest-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:56040761f6ca7abb036c7d288863bf143a9b166d824d98a604908ef20c6906e0", size = 5792872, upload-time = "2026-08-10T01:54:52.045Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ee/c5c74e3afb6dd0a25bca23bb4757d1a7135fab09a0a054036e837d49dd11/pyqwest-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:02a51df744521e45eb5379bc325b91de4512ec64a9f4aba3248f9166ec4e5b88", size = 5974192, upload-time = "2026-08-10T01:54:53.818Z" }, + { url = "https://files.pythonhosted.org/packages/6e/08/302960836e3e43ca3051313ee3b767534d1610bd3b8033c7bbad41b06e1b/pyqwest-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9a0e3123aee446d5f6e57c43cae87b4dab4b1af3fa970d114d74d5d4b9d075c", size = 4847050, upload-time = "2026-08-10T01:54:56.155Z" }, + { url = "https://files.pythonhosted.org/packages/fc/3e/a36940844325fbc59adf92116d3bcd1155cc2c72d1a390880886ca492bb4/pyqwest-0.9.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:fdab21bbedff4a21209135423f5f54f287a01dd6143cbd7aeab63d5d8c889654", size = 5246741, upload-time = "2026-08-10T01:54:57.697Z" }, + { url = "https://files.pythonhosted.org/packages/41/8d/efb9741449a81f2489f6929a7691a2145662c3650e06bcb139c64f543d0a/pyqwest-0.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:21a04ddf4497ef3c7fe36cdc473c094999b7710005d38bd8a0dfd4a3feace12f", size = 5102423, upload-time = "2026-08-10T01:54:59.253Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a8/0b667cf9c07e62861cfe24c09ad30357a9c2a0fc3863cfc5610af5b66e3e/pyqwest-0.9.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8c51c1a27ea529fffab90c127a045f0dfe1abc54d5b9ad5f4165e954439c9b2", size = 5627663, upload-time = "2026-08-10T01:55:01.036Z" }, + { url = "https://files.pythonhosted.org/packages/5f/7c/1f88be3d35afe92b79e361aa40faf3904e3a7852e8638a9d3d5c00891ab3/pyqwest-0.9.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f283ffc40a2774e0dc8fbb47e4a8f9f8a698a99feae3bf4ad75b340c98031b", size = 5572750, upload-time = "2026-08-10T01:55:03.163Z" }, + { url = "https://files.pythonhosted.org/packages/21/3e/98c1e151772e77dcf00cb8b1c070f2c8edcb15fe12fdee30c1022006ca8e/pyqwest-0.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f73d4a8e75b5860741fbd0623a0ce792a53336dbfec5f34736004acd5446843", size = 5790615, upload-time = "2026-08-10T01:55:04.758Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f1/12bb4b119cffb7fe5301b2d997ec9ffc1719637f6f4faf2ff2315e13b201/pyqwest-0.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e34d55a53492b44b82b690c2b11957fc35e99ebab4db2e9bcb2a1e2621d71b62", size = 5976516, upload-time = "2026-08-10T01:55:06.453Z" }, + { url = "https://files.pythonhosted.org/packages/53/55/046dbf68ccedd816770fd212c6c2612372a6f0d7ecf2d8273480db460ddf/pyqwest-0.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:48c367150783f6866d0af43c209cc405f8287743d7b2472c7d757fda2478fd57", size = 4841470, upload-time = "2026-08-10T01:55:08.035Z" }, + { url = "https://files.pythonhosted.org/packages/55/cd/38ff77d145385afd71b2dbd6eb116251ce3ffddbed0edde16c64394fbec2/pyqwest-0.9.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10fae206270355c993b49c49bf9778995167f5efe591807d50a8e682921d71ea", size = 5222689, upload-time = "2026-08-10T01:55:09.639Z" }, + { url = "https://files.pythonhosted.org/packages/82/95/d8c6804eeb72b7b89842b58f1f05d5cfda5be410c4a22442d2574d510d39/pyqwest-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5f475129fb792593222a9ce0fefbbc10bda1544a7411f3946d771c9a61c50e61", size = 5085846, upload-time = "2026-08-10T01:55:11.569Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e7/04fe8962ec416be0199093ebdd326653a5a7b8f53ddfdb29af81756f7c08/pyqwest-0.9.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78f6f676d349535cb094e8e3bcf33863238a142fb7dcc7afb9e61749e2351047", size = 5613296, upload-time = "2026-08-10T01:55:13.315Z" }, + { url = "https://files.pythonhosted.org/packages/03/0c/7a11728155a34838d2e7c78fb24c937f2cd844ec791ef416030c52f4bb5e/pyqwest-0.9.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f460f5fcdae8cc46e6a679128836f6cd47fbf4bf73e5c9d6ea868e4a90b2e7ee", size = 5557492, upload-time = "2026-08-10T01:55:15.103Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d5/8c6e68f0e978b5257b309809fec00d946c1ed20dacef97f25c23de5b91a9/pyqwest-0.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:cba6804151e973d27c99b9f02b3f0bcab7270c3f99725b508ada9c147365cabd", size = 5774204, upload-time = "2026-08-10T01:55:16.932Z" }, + { url = "https://files.pythonhosted.org/packages/17/20/fed89b62d50b4efad2ebe78c3867e0e402f824726652034728a5dd0a342e/pyqwest-0.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bc9dfeca326918d70a5368114de0fc627d9f64fafa36563ab484ae58ffe42c0d", size = 5964506, upload-time = "2026-08-10T01:55:19.031Z" }, + { url = "https://files.pythonhosted.org/packages/11/de/30575019efebae84502269d097e706e89a30631e3719218b053e4e0dc179/pyqwest-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384693adc0908f16324e5b508a3c25fb9a92b88f9bdf7ccb6820507db4a95888", size = 4809013, upload-time = "2026-08-10T01:55:21.042Z" }, + { url = "https://files.pythonhosted.org/packages/d0/4e/46822676c7cfafbf510f2af84b510b6f3dc1f3c4df781a837ff75c7410fc/pyqwest-0.9.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c174d8ebde256c457f455dcdedf59446528d84874cb5967041be0d1bda05929", size = 5227559, upload-time = "2026-08-10T01:55:22.625Z" }, + { url = "https://files.pythonhosted.org/packages/cc/80/9d98ad770043e5dd3ad7b765f2acf911bd11b113e0280565277d2e707864/pyqwest-0.9.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d5e56192b03bca7ec511c9af4ed80fa10fc1c1871d0ccfe49050632785b46099", size = 5109747, upload-time = "2026-08-10T01:55:24.393Z" }, + { url = "https://files.pythonhosted.org/packages/36/95/d1a88c8115c88f843b907e3d3bd1958eaef851cca023a47bba4257d974d9/pyqwest-0.9.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3fa3c93638fc645c7c626fe83032f53972673581878bd85702842f41665ad6c", size = 5631597, upload-time = "2026-08-10T01:55:26.19Z" }, + { url = "https://files.pythonhosted.org/packages/5c/3a/e568f9cf9ff75ca45ce7323ac916d076f9f4df4e957404ff0f0d3286dbe0/pyqwest-0.9.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:869d1c630cbff38510207c5b57139f319acca21ed978f9ce6648fcd868df5fbd", size = 5573130, upload-time = "2026-08-10T01:55:28.351Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c9/a261dbf9a4fd3cfa790221957c5cbe0d52e75ff7403dd91254aa977164b1/pyqwest-0.9.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a0bc2ba9b6d3103c6f0cce59cca9e2e294d1707bca435b471a1917db73010801", size = 5794377, upload-time = "2026-08-10T01:55:30.684Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a4/389a32479767cba432451f2913ff55bcab13981c5cf561028aa54ba97ae1/pyqwest-0.9.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:bfb04e3e91166fe6e4eecdcfccb0b2490da7629984b7988cfd4b11daa1dbb616", size = 5977476, upload-time = "2026-08-10T01:55:32.51Z" }, + { url = "https://files.pythonhosted.org/packages/14/26/91891131d47e1d9ad652ae6ff010bc9169ab051f20d888cd3471d87f070c/pyqwest-0.9.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fbff11bbffb572c084ccd76048c40bd57976b255d34f59280e56626f556cfaeb", size = 4833070, upload-time = "2026-08-10T01:55:34.506Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +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/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.4.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/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 = "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 = "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-click" +version = "1.8.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/a8/dcc0a8ec9e91d76ecad9413a84b6d3a3310c6111cfe012d75ed385c78d96/rich_click-1.8.9.tar.gz", hash = "sha256:fd98c0ab9ddc1cf9c0b7463f68daf28b4d0033a74214ceb02f761b3ff2af3136", size = 39378, upload-time = "2025-05-19T21:33:05.569Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/c2/9fce4c8a9587c4e90500114d742fe8ef0fd92d7bad29d136bb9941add271/rich_click-1.8.9-py3-none-any.whl", hash = "sha256:c3fa81ed8a671a10de65a9e20abf642cfdac6fdb882db1ef465ee33919fbcfe2", size = 36082, upload-time = "2025-05-19T21:33:04.195Z" }, +] + +[[package]] +name = "secretstorage" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "jeepney" }, +] +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 = "sentry-sdk" +version = "3.0.0a7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "opentelemetry-sdk" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/a5/747ab268f2cf3f4cb50af521e1a85a959411f74cf4a2ecf47782e1981cee/sentry_sdk-3.0.0a7.tar.gz", hash = "sha256:439a858d409b84407560df6b7ebb760acd9ad3d14e05cd919ae36b204e411cb2", size = 329999, upload-time = "2025-10-20T13:50:38.131Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/5e/23837eadf18ad4f9d5894c1210e6c2591136fd36cff338bedc90a1222bf2/sentry_sdk-3.0.0a7-py2.py3-none-any.whl", hash = "sha256:ada791a77c2d489e07b04c068e0fcacd6bbcfe1cb40b5f303207738baf38e868", size = 353618, upload-time = "2025-10-20T13:50:36.158Z" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[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 = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] + +[[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 = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, +] From 045e72f5096e792450fcea351b8adef7dea155b2 Mon Sep 17 00:00:00 2001 From: Niels Bantilan Date: Fri, 21 Aug 2026 00:34:59 -0400 Subject: [PATCH 2/2] docs(plugins): reword to satisfy codespell in the deepseek multi-agent example "tread on each other" tripped codespell (tread ==> thread, treat). Reworded to "interfere with each other" rather than widening the repo-wide ignore list. Co-Authored-By: Claude Opus 5 (1M context) --- plugins/agents/deepseek/examples/deepseek_multi_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/agents/deepseek/examples/deepseek_multi_agent.py b/plugins/agents/deepseek/examples/deepseek_multi_agent.py index ddef3fef3..1c0581a85 100644 --- a/plugins/agents/deepseek/examples/deepseek_multi_agent.py +++ b/plugins/agents/deepseek/examples/deepseek_multi_agent.py @@ -15,7 +15,7 @@ Every agent is a first-class Flyte node — independently retried, cached, resource-sized and observable — and the fan-out is real distributed parallelism, not just asyncio in one process. Each ``run_agent`` gets its own harness runtime -subprocess and its own workspace, so the agents cannot tread on each other. +subprocess and its own workspace, so the agents cannot interfere with each other. Run: flyte run deepseek_multi_agent.py research_pipeline --topic "The state of electric-vehicle batteries in 2025" (add `--local` right after `run` to execute locally instead of on the backend)